1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-09 07:25:23 +02:00

Merge pull request #17 from documize/github-extension

Github extension - secrets WIP
as requested
This commit is contained in:
Elliott Stoneham 2016-07-07 16:14:11 +01:00 committed by GitHub
commit 1436d37ff4
30 changed files with 918 additions and 689 deletions

View file

@ -15,427 +15,441 @@ import TooltipMixin from '../../../mixins/tooltip';
import SectionMixin from '../../../mixins/section'; import SectionMixin from '../../../mixins/section';
export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin, { export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin, {
sectionService: Ember.inject.service('section'), sectionService: Ember.inject.service('section'),
isDirty: false, isDirty: false,
busy: false, busy: false,
authenticated: false, authenticated: false,
config: {}, config: {},
owners: null, owners: null,
repos: null, repos: null,
noRepos: false, noRepos: false,
showCommits: false, showCommits: false,
showIssueNum: false, showIssueNum: false,
showLabels: false, showLabels: false,
didReceiveAttrs() { didReceiveAttrs() {
let self = this; let self = this;
let page = this.get('page'); let page = this.get('page');
if (is.undefined(this.get('config.clientId')) || is.undefined(this.get('config.callbackUrl'))) { if (is.undefined(this.get('config.clientId')) || is.undefined(this.get('config.callbackUrl'))) {
self.get('sectionService').fetch(page, "config", {}) self.get('sectionService').fetch(page, "config", {})
.then(function(cfg) { .then(function (cfg) {
let config = {}; let config = {};
config = { config = {
clientId: cfg.clientID, clientId: cfg.clientID,
callbackUrl: cfg.authorizationCallbackURL, callbackUrl: cfg.authorizationCallbackURL,
token: "", owner: null,
owner: null, owner_name: "",
owner_name: "", repo: null,
repo: null, repo_name: "",
repo_name: "", report: null,
report: null, lists: [],
lists: [], branch: "",
branch: "", branchURL: "",
branchURL: "", branchSince: "",
branchSince: "", branchLines: "30",
branchLines: "30", state: null,
state: null, issues: "",
issues: "", userId: "",
}; pageId: page.get('id'),
};
try {
let metaConfig = JSON.parse(self.get('meta.config')); try {
config.owner = metaConfig.owner; let metaConfig = JSON.parse(self.get('meta.config'));
config.repo = metaConfig.repo; config.owner = metaConfig.owner;
config.report = metaConfig.report; config.repo = metaConfig.repo;
config.lists = metaConfig.lists; config.report = metaConfig.report;
config.branchSince = metaConfig.branchSince; config.lists = metaConfig.lists;
config.branchLines = metaConfig.branchLines; config.branchSince = metaConfig.branchSince;
config.state = metaConfig.state; config.branchLines = metaConfig.branchLines;
config.issues = metaConfig.issues; config.state = metaConfig.state;
} catch (e) {} config.issues = metaConfig.issues;
config.userId = metaConfig.userId;
self.set('config', config); config.pageId = metaConfig.pageId;
} catch (e) {}
// On auth callback capture code
let code = window.location.search; self.set('config', config);
self.set('config.pageId', page.get('id'));
if (is.not.undefined(code) && is.not.null(code)) {
let tok = code.replace("?code=", ""); // On auth callback capture code
if (is.not.empty(code)) { let code = window.location.search;
self.set('config.token', tok);
self.send('authStage2'); if (is.not.undefined(code) && is.not.null(code) && is.not.empty(code) && code !== "") {
} let tok = code.replace("?code=", "");
} else { self.get('sectionService').fetch(page, "saveSecret", { "token": tok })
if (self.get('config.token') === "") { .then(function () {
self.send('auth'); console.log("github auth code saved to db");
} self.send('authStage2');
} }, function (error) { //jshint ignore: line
}, function(error) { //jshint ignore: line console.log(error);
console.log(error); self.send('auth');
}); });
} } else {
}, if (config.userId !== self.session.user.id) {
console.log("github auth wrong user ID, switching");
willDestroyElement() { self.set('config.userId', self.session.user.id);
this.destroyTooltips(); }
}, self.get('sectionService').fetch(page, "checkAuth", self.get('config'))
.then(function () {
getOwnerLists() { console.log("github auth code valid");
this.set('busy', true); self.send('authStage2');
}, function (error) { //jshint ignore: line
let self = this; console.log(error);
let owners = this.get('owners'); self.send('auth'); // require auth if the db token is invalid
let thisOwner = this.get('config.owner'); });
let page = this.get('page'); }
}, function (error) { //jshint ignore: line
if (is.null(thisOwner) || is.undefined(thisOwner)) { console.log(error);
if (owners.length) { });
thisOwner = owners[0]; }
this.set('config.owner', thisOwner); },
}
} else { willDestroyElement() {
this.set('config.owner', owners.findBy('id', thisOwner.id)); this.destroyTooltips();
} },
this.set('owner', thisOwner); getOwnerLists() {
this.set('busy', true);
this.get('sectionService').fetch(page, "repos", self.get('config'))
.then(function(lists) { let self = this;
self.set('busy', false); let owners = this.get('owners');
self.set('repos', lists); let thisOwner = this.get('config.owner');
self.getRepoLists(); let page = this.get('page');
}, function(error) { //jshint ignore: line
self.set('busy', false); if (is.null(thisOwner) || is.undefined(thisOwner)) {
self.set('authenticated', false); if (owners.length) {
self.showNotification("Unable to fetch repositories"); thisOwner = owners[0];
console.log(error); this.set('config.owner', thisOwner);
}); }
}, } else {
this.set('config.owner', owners.findBy('id', thisOwner.id));
getRepoLists() { }
this.set('busy', true);
this.set('owner', thisOwner);
let repos = this.get('repos');
let thisRepo = this.get('config.repo'); this.get('sectionService').fetch(page, "repos", self.get('config'))
.then(function (lists) {
if (is.null(repos) || is.undefined(repos) || repos.length === 0) { self.set('busy', false);
this.set('noRepos', true); self.set('repos', lists);
return; self.getRepoLists();
} }, function (error) { //jshint ignore: line
self.set('busy', false);
this.set('noRepos', false); self.set('authenticated', false);
self.showNotification("Unable to fetch repositories");
if (is.null(thisRepo) || is.undefined(thisRepo) || thisRepo.owner !== this.get('config.owner').name) { console.log(error);
if (repos.length) { });
thisRepo = repos[0]; },
this.set('config.repo', thisRepo);
} getRepoLists() {
} else { this.set('busy', true);
this.set('config.repo', repos.findBy('id', thisRepo.id));
} let repos = this.get('repos');
let thisRepo = this.get('config.repo');
this.set('repo', thisRepo);
if (is.null(repos) || is.undefined(repos) || repos.length === 0) {
this.getReportLists(); this.set('noRepos', true);
}, return;
}
getReportLists() { this.set('noRepos', false);
let reports = [];
reports[0] = { if (is.null(thisRepo) || is.undefined(thisRepo) || thisRepo.owner !== this.get('config.owner').name) {
id: "commits_data", // used as method for fetching Go data if (repos.length) {
name: "Commits on a branch" thisRepo = repos[0];
}; this.set('config.repo', thisRepo);
reports[1] = { }
id: "issues_data", // used as method for fetching Go data } else {
name: "Issues" this.set('config.repo', repos.findBy('id', thisRepo.id));
}; }
this.set("reports", reports); this.set('repo', thisRepo);
let thisReport = this.get('config.report'); this.getReportLists();
},
if (is.null(thisReport) || is.undefined(thisReport)) {
thisReport = reports[0]; getReportLists() {
this.set('config.report', thisReport); let reports = [];
} else { reports[0] = {
this.set('config.report', reports.findBy('id', thisReport.id)); id: "commitsData", // used as method for fetching Go data
} name: "Commits on a branch"
};
this.set('report', thisReport); reports[1] = {
id: "issuesData", // used as method for fetching Go data
this.renderSwitch(thisReport); name: "Issues"
};
},
this.set("reports", reports);
renderSwitch(thisReport) {
let thisReport = this.get('config.report');
if (is.undefined(this.get('initDateTimePicker'))) {
$.datetimepicker.setLocale('en'); if (is.null(thisReport) || is.undefined(thisReport)) {
$('#branch-since').datetimepicker(); thisReport = reports[0];
this.set('initDateTimePicker', "Done"); this.set('config.report', thisReport);
} } else {
this.set('config.report', reports.findBy('id', thisReport.id));
let bl = this.get('config.branchLines'); }
if (is.undefined(bl) || bl === "" || bl <= 0) {
this.set('config.branchLines', "30"); this.set('report', thisReport);
}
this.renderSwitch(thisReport);
this.set('showCommits', false);
this.set('showLabels', false); },
switch (thisReport.id) {
case 'commits_data': renderSwitch(thisReport) {
this.set('showCommits', true);
this.getBranchLists(); if (is.undefined(this.get('initDateTimePicker'))) {
break; $.datetimepicker.setLocale('en');
case "issues_data": $('#branch-since').datetimepicker();
this.set('showLabels', true); this.set('initDateTimePicker', "Done");
this.getLabelLists(); }
break;
} let bl = this.get('config.branchLines');
}, if (is.undefined(bl) || bl === "" || bl <= 0) {
this.set('config.branchLines', "30");
getBranchLists() { }
this.set('busy', true);
this.set('showCommits', false);
let self = this; this.set('showLabels', false);
let page = this.get('page'); switch (thisReport.id) {
case 'commitsData':
this.get('sectionService').fetch(page, "branches", self.get('config')) this.set('showCommits', true);
.then(function(lists) { this.getBranchLists();
let savedLists = self.get('config.lists'); break;
if (savedLists === null) { case 'issuesData':
savedLists = []; this.set('showLabels', true);
} this.getLabelLists();
break;
if (lists.length > 0) { }
let noIncluded = true; },
lists.forEach(function(list) { getBranchLists() {
let included = false; this.set('busy', true);
var saved;
if (is.not.undefined(savedLists)) { let self = this;
saved = savedLists.findBy("id", list.id); let page = this.get('page');
}
if (is.not.undefined(saved)) { this.get('sectionService').fetch(page, "branches", self.get('config'))
included = saved.included; .then(function (lists) {
noIncluded = false; let savedLists = self.get('config.lists');
} if (savedLists === null) {
list.included = included; savedLists = [];
}); }
if (noIncluded) { if (lists.length > 0) {
lists[0].included = true; // make the first entry the default let noIncluded = true;
}
} lists.forEach(function (list) {
let included = false;
self.set('config.lists', lists); var saved;
self.set('busy', false); if (is.not.undefined(savedLists)) {
}, function(error) { //jshint ignore: line saved = savedLists.findBy("id", list.id);
self.set('busy', false); }
self.set('authenticated', false); if (is.not.undefined(saved)) {
self.showNotification("Unable to fetch repository branches"); included = saved.included;
console.log(error); noIncluded = false;
}); }
}, list.included = included;
});
getLabelLists() {
this.set('busy', true); if (noIncluded) {
lists[0].included = true; // make the first entry the default
let self = this; }
let page = this.get('page'); }
let states = []; self.set('config.lists', lists);
states[0] = { self.set('busy', false);
id: "open", }, function (error) { //jshint ignore: line
name: "Show Open Issues" self.set('busy', false);
}; self.set('authenticated', false);
states[1] = { self.showNotification("Unable to fetch repository branches");
id: "closed", console.log(error);
name: "Show Closed Issues" });
}; },
states[2] = {
id: "all", getLabelLists() {
name: "Show All Issues" this.set('busy', true);
};
let self = this;
this.set("states", states); let page = this.get('page');
let thisState = this.get('config.state'); let states = [];
states[0] = {
if (is.null(thisState) || is.undefined(thisState)) { id: "open",
thisState = states[0]; name: "Show Open Issues"
this.set('config.state', thisState); };
} else { states[1] = {
this.set('config.state', states.findBy('id', thisState.id)); id: "closed",
} name: "Show Closed Issues"
};
this.set('state', thisState); states[2] = {
id: "all",
this.get('sectionService').fetch(page, "labels", self.get('config')) name: "Show All Issues"
.then(function(lists) { };
let savedLists = self.get('config.lists');
if (savedLists === null) { this.set("states", states);
savedLists = [];
} let thisState = this.get('config.state');
if (lists.length > 0) { if (is.null(thisState) || is.undefined(thisState)) {
lists.forEach(function(list) { thisState = states[0];
var saved; this.set('config.state', thisState);
if (is.not.undefined(savedLists)) { } else {
saved = savedLists.findBy("id", list.id); this.set('config.state', states.findBy('id', thisState.id));
} }
let included = false;
if (is.not.undefined(saved)) { this.set('state', thisState);
included = saved.included;
} this.get('sectionService').fetch(page, "labels", self.get('config'))
list.included = included; .then(function (lists) {
}); let savedLists = self.get('config.lists');
} if (savedLists === null) {
savedLists = [];
self.set('config.lists', lists); }
self.set('busy', false);
}, function(error) { //jshint ignore: line if (lists.length > 0) {
self.set('busy', false); lists.forEach(function (list) {
self.set('authenticated', false); var saved;
self.showNotification("Unable to fetch repository labels"); if (is.not.undefined(savedLists)) {
console.log(error); saved = savedLists.findBy("id", list.id);
}); }
}, let included = false;
if (is.not.undefined(saved)) {
actions: { included = saved.included;
isDirty() { }
return this.get('isDirty'); list.included = included;
}, });
}
onListCheckbox(id) {
let lists = this.get('config.lists'); self.set('config.lists', lists);
let list = lists.findBy('id', id); self.set('busy', false);
}, function (error) { //jshint ignore: line
// restore the list of branches to the default state self.set('busy', false);
lists.forEach(function(lst) { self.set('authenticated', false);
Ember.set(lst, 'included', false); self.showNotification("Unable to fetch repository labels");
}); console.log(error);
});
if (list !== null) { },
Ember.set(list, 'included', !list.included);
} actions: {
}, isDirty() {
return this.get('isDirty');
onLabelCheckbox(id) { },
let lists = this.get('config.lists');
let list = lists.findBy('id', id); onListCheckbox(id) {
let lists = this.get('config.lists');
if (list !== null) { let list = lists.findBy('id', id);
Ember.set(list, 'included', !list.included);
} // restore the list of branches to the default state
}, lists.forEach(function (lst) {
Ember.set(lst, 'included', false);
});
authStage2() {
let self = this; if (list !== null) {
self.set('authenticated', true); Ember.set(list, 'included', !list.included);
self.set('busy', true); }
let page = this.get('page'); },
self.get('sectionService').fetch(page, "owners", self.get('config')) onLabelCheckbox(id) {
.then(function(owners) { let lists = this.get('config.lists');
self.set('busy', false); let list = lists.findBy('id', id);
self.set('owners', owners);
self.getOwnerLists(); if (list !== null) {
}, function(error) { //jshint ignore: line Ember.set(list, 'included', !list.included);
self.set('busy', false); }
self.set('authenticated', false); },
self.showNotification("Unable to fetch owners");
console.log(error); authStage2() {
}); let self = this;
self.set('config.userId', this.session.user.id);
}, self.set('authenticated', true);
self.set('busy', true);
auth() { let page = this.get('page');
let self = this; self.get('sectionService').fetch(page, "owners", self.get('config'))
self.set('busy', true); .then(function (owners) {
self.set('authenticated', false); self.set('busy', false);
let target = "https://github.com/login/oauth/authorize?client_id=" + self.get('config.clientId') + self.set('owners', owners);
"&scope=repo&redirect_uri=" + encodeURIComponent(self.get('config.callbackUrl')) + self.getOwnerLists();
"&state=" + encodeURIComponent(window.location.href); }, function (error) { //jshint ignore: line
window.location.href = target; self.set('busy', false);
self.set('authenticated', false);
}, self.showNotification("Unable to fetch owners");
console.log(error);
onOwnerChange(thisOwner) { });
this.set('isDirty', true);
this.set('config.owner', thisOwner); },
this.set('config.repos', []);
this.set('config.lists', []); auth() {
this.getOwnerLists(); let self = this;
}, self.set('busy', true);
self.set('authenticated', false);
onRepoChange(thisRepo) { let target = "https://github.com/login/oauth/authorize?client_id=" + self.get('config.clientId') +
this.set('isDirty', true); "&scope=repo&redirect_uri=" + encodeURIComponent(self.get('config.callbackUrl')) +
this.set('config.repo', thisRepo); "&state=" + encodeURIComponent(window.location.href);
this.set('config.lists', []); window.location.href = target;
this.getRepoLists();
}, },
onReportChange(thisReport) { onOwnerChange(thisOwner) {
this.set('isDirty', true); this.set('isDirty', true);
this.set('config.report', thisReport); this.set('config.owner', thisOwner);
this.getReportLists(); this.set('config.repos', []);
}, this.set('config.lists', []);
this.getOwnerLists();
onStateChange(thisState) { },
this.set('config.state', thisState);
}, onRepoChange(thisRepo) {
this.set('isDirty', true);
this.set('config.repo', thisRepo);
onCancel() { this.set('config.lists', []);
this.attrs.onCancel(); this.getRepoLists();
}, },
onAction(title) { onReportChange(thisReport) {
this.set('busy', true); this.set('isDirty', true);
this.set('config.report', thisReport);
let thisLines = this.get('config.branchLines'); this.getReportLists();
if (is.undefined(thisLines) || thisLines === "") { },
this.set('config.branchLines', 30);
} else if (thisLines < 1) { onStateChange(thisState) {
this.set('config.branchLines', 1); this.set('config.state', thisState);
} else if (thisLines > 100) { },
this.set('config.branchLines', 100);
} onCancel() {
this.attrs.onCancel();
let self = this; },
let page = this.get('page');
let meta = this.get('meta'); onAction(title) {
page.set('title', title); this.set('busy', true);
meta.set('rawBody', '');
meta.set('config', JSON.stringify(this.get('config'))); let thisLines = this.get('config.branchLines');
meta.set('externalSource', true); if (is.undefined(thisLines) || thisLines === "") {
this.set('config.branchLines', 30);
let thisReport = this.get('config.report'); } else if (thisLines < 1) {
this.get('sectionService').fetch(page, thisReport.id, this.get('config')) this.set('config.branchLines', 1);
.then(function(response) { } else if (thisLines > 100) {
meta.set('rawBody', JSON.stringify(response)); this.set('config.branchLines', 100);
self.set('busy', false); }
self.attrs.onAction(page, meta);
}, function(reason) { //jshint ignore: line let self = this;
self.set('busy', false); let page = this.get('page');
self.attrs.onAction(page, meta); let meta = this.get('meta');
}); page.set('title', title);
} meta.set('rawBody', '');
} meta.set('config', JSON.stringify(this.get('config')));
meta.set('externalSource', true);
let thisReport = this.get('config.report');
this.get('sectionService').fetch(page, thisReport.id, this.get('config'))
.then(function (response) {
meta.set('rawBody', JSON.stringify(response));
self.set('busy', false);
self.attrs.onAction(page, meta);
}, function (reason) { //jshint ignore: line
self.set('busy', false);
self.attrs.onAction(page, meta);
});
}
}
}); });

View file

@ -16,40 +16,40 @@ import SectionMixin from '../../../mixins/section';
import netUtil from '../../../utils/net'; import netUtil from '../../../utils/net';
export default Ember.Component.extend(SectionMixin, 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,
authenticated: false, authenticated: false,
config: {}, config: {},
items: {}, items: {},
didReceiveAttrs() { didReceiveAttrs() {
let config = {}; let config = {};
try { try {
config = JSON.parse(this.get('meta.config')); config = JSON.parse(this.get('meta.config'));
} catch (e) {} } catch (e) {}
if (is.empty(config)) { if (is.empty(config)) {
config = { config = {
APIToken: "", APIToken: "",
query: "", query: "",
max: 10, max: 10,
group: null, group: null,
system: null system: null
}; };
} }
this.set('config', config); this.set('config', config);
if (this.get('config.APIToken').length > 0) { if (this.get('config.APIToken').length > 0) {
this.send('auth'); this.send('auth');
} }
}, },
willDestroyElement() { willDestroyElement() {
this.destroyTooltips(); this.destroyTooltips();
}, },
displayError(reason) { displayError(reason) {
if (netUtil.isAjaxAccessError(reason)) { if (netUtil.isAjaxAccessError(reason)) {
@ -59,98 +59,102 @@ export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin,
} }
}, },
actions: { actions: {
isDirty() { isDirty() {
return this.get('isDirty'); return this.get('isDirty');
}, },
auth() { auth() {
console.log(this.get('config'));
// missing data? // missing data?
this.set('config.APIToken', this.get('config.APIToken').trim()); this.set('config.APIToken', this.get('config.APIToken').trim());
if (is.empty(this.get('config.APIToken'))) { if (is.empty(this.get('config.APIToken'))) {
$("#papertrail-apitoken").addClass("error").focus(); $("#papertrail-apitoken").addClass("error").focus();
return; return;
} }
let page = this.get('page'); let page = this.get('page');
let config = this.get('config'); let config = this.get('config');
let self = this; let self = this;
this.set('waiting', true); this.set('waiting', true);
this.get('sectionService').fetch(page, "auth", config) this.get('sectionService').fetch(page, "auth", config)
.then(function(response) { .then(function (response) {
self.set('authenticated', true); self.set('authenticated', true);
self.set('items', response); self.set('items', response);
self.set('config.APIToken', '********'); // reset the api token once it has been sent to the host
self.get('sectionService').fetch(page, "options", config) self.get('sectionService').fetch(page, "options", config)
.then(function(response) { .then(function (response) {
self.set('options', response); self.set('options', response);
self.set('waiting', false); self.set('waiting', false);
let options = self.get('options'); let options = self.get('options');
let group = _.findWhere(options.groups, {id: config.group.id}); let group = _.findWhere(options.groups, { id: config.group.id });
if (is.not.undefined(group)) { if (is.not.undefined(group)) {
Ember.set(config, 'group', group); Ember.set(config, 'group', group);
} }
}, function(reason) { //jshint ignore: line }, function (reason) { //jshint ignore: line
self.set('waiting', false); self.set('waiting', false);
self.displayError(reason);
});
}, function (reason) { //jshint ignore: line
self.set('authenticated', false);
self.set('waiting', false);
self.set('config.APIToken', ''); // clear the api token
self.displayError(reason); self.displayError(reason);
}); $("#papertrail-apitoken").addClass("error").focus();
}, function(reason) { //jshint ignore: line });
self.set('authenticated', false); },
self.set('waiting', false);
self.displayError(reason);
});
},
onGroupsChange(group) { onGroupsChange(group) {
let config = this.get('config'); let config = this.get('config');
let page = this.get('page'); let page = this.get('page');
let self = this; let self = this;
this.set('isDirty', true); this.set('isDirty', true);
this.set('config.group', group); this.set('config.group', group);
this.set('waiting', true); this.set('waiting', true);
this.get('sectionService').fetch(page, "auth", config) this.get('sectionService').fetch(page, "auth", config)
.then(function(response) { .then(function (response) {
self.set('waiting', false);
self.set('items', response);
}, function (reason) { //jshint ignore: line
self.set('waiting', false); self.set('waiting', false);
self.set('items', response);
}, function(reason) { //jshint ignore: line
self.set('waiting', false);
self.displayError(reason); self.displayError(reason);
}); });
}, },
onSystemsChange(system) { onSystemsChange(system) {
let config = this.get('config'); let config = this.get('config');
let page = this.get('page'); let page = this.get('page');
let self = this; let self = this;
this.set('isDirty', true); this.set('isDirty', true);
this.set('config.system', system); this.set('config.system', system);
this.set('waiting', true); this.set('waiting', true);
this.get('sectionService').fetch(page, "auth", config) this.get('sectionService').fetch(page, "auth", config)
.then(function(response) { .then(function (response) {
self.set('waiting', false);
self.set('items', response);
}, function (reason) { //jshint ignore: line
self.set('waiting', false); self.set('waiting', false);
self.set('items', response);
}, function(reason) { //jshint ignore: line
self.set('waiting', false);
self.displayError(reason); self.displayError(reason);
}); });
}, },
onCancel() { onCancel() {
this.attrs.onCancel(); this.attrs.onCancel();
}, },
onAction(title) { onAction(title) {
let self = this; 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('externalSource', true); meta.set('externalSource', true);
let config = this.get('config'); let config = this.get('config');
let max = 10; let max = 10;
@ -161,25 +165,25 @@ export default Ember.Component.extend(SectionMixin, NotifierMixin, TooltipMixin,
Ember.set(config, 'max', max); Ember.set(config, 'max', max);
this.set('waiting', true); this.set('waiting', true);
this.get('sectionService').fetch(page, "auth", this.get('config')) this.get('sectionService').fetch(page, "auth", this.get('config'))
.then(function(response) { .then(function (response) {
self.set('items', response); self.set('items', response);
let items = self.get('items'); let items = self.get('items');
if (items.events.length > max) { if (items.events.length > max) {
items.events = items.events.slice(0, max); items.events = items.events.slice(0, max);
} }
meta.set('config', JSON.stringify(config)); meta.set('config', JSON.stringify(config));
meta.set('rawBody', JSON.stringify(items)); meta.set('rawBody', JSON.stringify(items));
self.set('waiting', false); self.set('waiting', false);
self.attrs.onAction(page, meta); self.attrs.onAction(page, meta);
}, function(reason) { //jshint ignore: line }, function (reason) { //jshint ignore: line
self.set('authenticated', false); self.set('authenticated', false);
self.set('waiting', false); self.set('waiting', false);
self.showNotification(`Something went wrong, try again!`); self.showNotification(`Something went wrong, try again!`);
}); });
} }
} }
}); });

View file

@ -14,61 +14,65 @@ import netUtil from '../utils/net';
import config from '../config/environment'; import config from '../config/environment';
export default Ember.Service.extend({ export default Ember.Service.extend({
sessionService: Ember.inject.service('session'), sessionService: Ember.inject.service('session'),
ready: false, ready: false,
enabled: config.APP.auditEnabled, enabled: config.APP.auditEnabled,
appId: config.APP.intercomKey, appId: config.APP.intercomKey,
init() { init() {
this.start(); this.start();
}, },
record(id) { record(id) {
if (!this.get('enabled') || is.empty(this.get('appId'))) { if (!this.get('enabled') || this.get('appId').length === 0) {
return; return;
} }
if (!this.get('ready')) { if (!this.get('ready')) {
this.start(); this.start();
} }
Intercom('trackEvent', id); //jshint ignore: line Intercom('trackEvent', id); //jshint ignore: line
Intercom('update'); //jshint ignore: line Intercom('update'); //jshint ignore: line
}, },
stop() { stop() {
Intercom('shutdown'); //jshint ignore: line if (!this.get('enabled') || this.get('appId').length === 0) {
}, return;
}
start() { Intercom('shutdown'); //jshint ignore: line
let session = this.get('sessionService'); },
if (this.get('appId') === "" || !this.get('enabled') || !session.authenticated || this.get('ready')) { start() {
return; let session = this.get('sessionService');
}
this.set('ready', true); if (this.get('appId') === "" || !this.get('enabled') || !session.authenticated || this.get('ready')) {
return;
}
window.intercomSettings = { this.set('ready', true);
app_id: this.get('appId'),
name: session.user.firstname + " " + session.user.lastname,
email: session.user.email,
user_id: session.user.id,
"administrator": session.user.admin,
company: {
id: session.get('appMeta.orgId'),
name: session.get('appMeta.title').string,
"domain": netUtil.getSubdomain(),
"version": session.get('appMeta.version')
}
};
if (!session.get('isMobile')) { window.intercomSettings = {
window.intercomSettings.widget = { app_id: this.get('appId'),
activator: "#IntercomDefaultWidget" name: session.user.firstname + " " + session.user.lastname,
}; email: session.user.email,
} user_id: session.user.id,
"administrator": session.user.admin,
company: {
id: session.get('appMeta.orgId'),
name: session.get('appMeta.title').string,
"domain": netUtil.getSubdomain(),
"version": session.get('appMeta.version')
}
};
window.Intercom('boot', window.intercomSettings); if (!session.get('isMobile')) {
}, window.intercomSettings.widget = {
}); activator: "#IntercomDefaultWidget"
};
}
window.Intercom('boot', window.intercomSettings);
},
});

View file

@ -4,7 +4,11 @@
<div class="input-form"> <div class="input-form">
<form {{action 'auth' on="submit"}}> <form {{action 'auth' on="submit"}}>
<div class="heading"> <div class="heading">
<div class="title">Papertrail Authentication</div> <div class="title">Papertrail Authentication
{{#if authenticated}}
Complete
{{/if}}
</div>
<div class="tip">Provide your Papertrail API token</div> <div class="tip">Provide your Papertrail API token</div>
</div> </div>
<div class="input-control"> <div class="input-control">
@ -12,7 +16,11 @@
<div class="tip">API Token (from your profile)</div> <div class="tip">API Token (from your profile)</div>
{{focus-input id="papertrail-apitoken" type="password" value=config.APIToken readonly=isReadonly}} {{focus-input id="papertrail-apitoken" type="password" value=config.APIToken readonly=isReadonly}}
</div> </div>
<div class="regular-button button-blue" {{ action 'auth' }}>Authenticate</div> {{#if authenticated}}
<div class="regular-button button-blue" {{ action 'auth' }}>Re-Authenticate</div>
{{else}}
<div class="regular-button button-blue" {{ action 'auth' }}>Authenticate</div>
{{/if}}
</form> </form>
</div> </div>
</div> </div>

View file

@ -10,37 +10,37 @@
// https://documize.com // https://documize.com
function getSubdomain() { function getSubdomain() {
if (is.ipv4(window.location.host)) { if (is.ipv4(window.location.host)) {
return ""; return "";
} }
let domain = ""; let domain = "";
let parts = window.location.host.split("."); let parts = window.location.host.split(".");
if (parts.length > 1) { if (parts.length > 1) {
domain = parts[0].toLowerCase(); domain = parts[0].toLowerCase();
} }
return domain; return domain;
} }
function getAppUrl(domain) { function getAppUrl(domain) {
let parts = window.location.host.split("."); let parts = window.location.host.split(".");
parts.removeAt(0); parts.removeAt(0);
let leftOvers = parts.join("."); let leftOvers = parts.join(".");
if (is.empty(domain)) { if (is.empty(domain)) {
domain = ""; domain = "";
} else { } else {
domain = domain + "."; domain = domain + ".";
} }
return window.location.protocol + "//" + domain + leftOvers; return window.location.protocol + "//" + domain + leftOvers;
} }
function isAjaxAccessError(reason) { function isAjaxAccessError(reason) {
if (typeof reason === "undefined") { if (typeof reason === "undefined" || typeof reason.errors === "undefined") {
return false; return false;
} }
@ -52,7 +52,7 @@ function isAjaxAccessError(reason) {
} }
export default { export default {
getSubdomain, getSubdomain,
getAppUrl, getAppUrl,
isAjaxAccessError, isAjaxAccessError,
}; };

View file

@ -79,6 +79,8 @@ func AddDocumentPage(w http.ResponseWriter, r *http.Request) {
model.Meta.PageID = pageID model.Meta.PageID = pageID
model.Page.SetDefaults() model.Page.SetDefaults()
model.Meta.SetDefaults() model.Meta.SetDefaults()
model.Meta.OrgID = p.Context.OrgID
model.Meta.UserID = p.Context.UserID
// page.Title = template.HTMLEscapeString(page.Title) // page.Title = template.HTMLEscapeString(page.Title)
tx, err := request.Db.Beginx() tx, err := request.Db.Beginx()
@ -90,7 +92,8 @@ func AddDocumentPage(w http.ResponseWriter, r *http.Request) {
p.Context.Transaction = tx p.Context.Transaction = tx
output, ok := provider.Render(model.Page.ContentType, model.Meta.Config, model.Meta.RawBody) output, ok := provider.Render(model.Page.ContentType,
provider.NewContext(model.Meta.OrgID, model.Meta.UserID), model.Meta.Config, model.Meta.RawBody)
if !ok { if !ok {
log.ErrorString("provider.Render could not find: " + model.Page.ContentType) log.ErrorString("provider.Render could not find: " + model.Page.ContentType)
} }
@ -374,7 +377,7 @@ func DeleteDocumentPages(w http.ResponseWriter, r *http.Request) {
writeSuccessEmptyJSON(w) writeSuccessEmptyJSON(w)
} }
// UpdateDocumentPage will persiste changed page and note the fact // UpdateDocumentPage will persist changed page and note the fact
// that this is a new revision. If the page is the first in a document // that this is a new revision. If the page is the first in a document
// then the corresponding document title will also be changed. // then the corresponding document title will also be changed.
func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) { func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) {
@ -432,7 +435,15 @@ func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) {
model.Page.SetDefaults() model.Page.SetDefaults()
model.Meta.SetDefaults() model.Meta.SetDefaults()
output, ok := provider.Render(model.Page.ContentType, model.Meta.Config, model.Meta.RawBody) oldPageMeta, err := p.GetPageMeta(pageID)
if err != nil {
log.Error("unable to fetch old pagemeta record", err)
writeBadRequestError(w, method, err.Error())
return
}
output, ok := provider.Render(model.Page.ContentType, provider.NewContext(model.Meta.OrgID, oldPageMeta.UserID), model.Meta.Config, model.Meta.RawBody)
if !ok { if !ok {
log.ErrorString("provider.Render could not find: " + model.Page.ContentType) log.ErrorString("provider.Render could not find: " + model.Page.ContentType)
} }
@ -452,7 +463,7 @@ func UpdateDocumentPage(w http.ResponseWriter, r *http.Request) {
return return
} }
err = p.UpdatePageMeta(model.Meta) err = p.UpdatePageMeta(model.Meta, true) // change the UserID to the current one
log.IfErr(tx.Commit()) log.IfErr(tx.Commit())

View file

@ -70,7 +70,7 @@ func RunSectionCommand(w http.ResponseWriter, r *http.Request) {
return return
} }
if !provider.Command(sectionName, w, r) { if !provider.Command(sectionName, provider.NewContext(p.Context.OrgID, p.Context.UserID), w, r) {
log.ErrorString("Unable to run provider.Command() for: " + sectionName) log.ErrorString("Unable to run provider.Command() for: " + sectionName)
writeNotFoundError(w, "RunSectionCommand", sectionName) writeNotFoundError(w, "RunSectionCommand", sectionName)
} }
@ -125,14 +125,16 @@ func RefreshSections(w http.ResponseWriter, r *http.Request) {
return return
} }
pcontext := provider.NewContext(pm.OrgID, pm.UserID)
// Ask for data refresh // Ask for data refresh
data, ok := provider.Refresh(page.ContentType, pm.Config, pm.RawBody) data, ok := provider.Refresh(page.ContentType, pcontext, pm.Config, pm.RawBody)
if !ok { if !ok {
log.ErrorString("provider.Refresh could not find: " + page.ContentType) log.ErrorString("provider.Refresh could not find: " + page.ContentType)
} }
// Render again // Render again
body, ok := provider.Render(page.ContentType, pm.Config, data) body, ok := provider.Render(page.ContentType, pcontext, pm.Config, data)
if !ok { if !ok {
log.ErrorString("provider.Render could not find: " + page.ContentType) log.ErrorString("provider.Render could not find: " + page.ContentType)
} }
@ -153,7 +155,7 @@ func RefreshSections(w http.ResponseWriter, r *http.Request) {
return return
} }
err = p.UpdatePageMeta(pm) err = p.UpdatePageMeta(pm, false) // do not change the UserID on this PageMeta
if err != nil { if err != nil {
writeGeneralSQLError(w, method, err) writeGeneralSQLError(w, method, err)

View file

@ -204,6 +204,7 @@ type PageMeta struct {
Created time.Time `json:"created"` Created time.Time `json:"created"`
Revised time.Time `json:"revised"` Revised time.Time `json:"revised"`
OrgID string `json:"orgId"` OrgID string `json:"orgId"`
UserID string `json:"userId"`
DocumentID string `json:"documentId"` DocumentID string `json:"documentId"`
PageID string `json:"pageId"` PageID string `json:"pageId"`
RawBody string `json:"rawBody"` // a blob of data RawBody string `json:"rawBody"` // a blob of data

View file

@ -1,11 +1,11 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved. // Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
// //
// This software (Documize Community Edition) is licensed under // This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
// //
// You can operate outside the AGPL restrictions by purchasing // You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license // Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>. // by contacting <sales@documize.com>.
// //
// https://documize.com // https://documize.com
@ -13,6 +13,7 @@ package request
import ( import (
"bytes" "bytes"
"errors"
"github.com/documize/community/wordsmith/utility" "github.com/documize/community/wordsmith/utility"
) )
@ -65,3 +66,64 @@ func ConfigString(area, path string) (ret string) {
//fmt.Println("DEBUG ConfigString " + sql + " => " + ret) //fmt.Println("DEBUG ConfigString " + sql + " => " + ret)
return ret return ret
} }
// UserConfigGetJSON fetches a configuration JSON element from the userconfig table for a given orgid/userid combination.
// Errors return the empty string. A blank path returns the whole JSON object, as JSON.
func UserConfigGetJSON(orgid, userid, area, path string) (ret string) {
if Db == nil {
return ""
}
if path != "" {
path = "." + path
}
sql := "SELECT JSON_EXTRACT(`config`,'$" + path + "') FROM `userconfig` WHERE `key` = '" + area +
"' AND `orgid` = '" + orgid + "' AND `userid` = '" + userid + "';"
stmt, err := Db.Preparex(sql)
if err != nil {
//fmt.Printf("DEBUG: Unable to prepare select SQL for ConfigString: %s -- error: %v\n", sql, err)
return ""
}
defer utility.Close(stmt)
var item = make([]uint8, 0)
err = stmt.Get(&item)
if err != nil {
//fmt.Printf("DEBUG: Unable to prepare execute SQL for ConfigString: %s -- error: %v\n", sql, err)
return ""
}
if len(item) > 1 {
q := []byte(`"`)
ret = string(bytes.TrimPrefix(bytes.TrimSuffix(item, q), q))
}
//fmt.Println("DEBUG UserConfigString " + sql + " => " + ret)
return ret
}
// UserConfigSetJSON writes a configuration JSON element to the userconfig table for the current user.
func UserConfigSetJSON(orgid, userid, area, json string) error {
if Db == nil {
return errors.New("no database")
}
if area == "" {
return errors.New("no area")
}
sql := "INSERT INTO `userconfig` (`orgid`,`userid`,`key`,`config`) " +
"VALUES ('" + orgid + "','" + userid + "','" + area + "','" + json +
"') ON DUPLICATE KEY UPDATE `config`='" + json + "';"
stmt, err := Db.Preparex(sql)
if err != nil {
//fmt.Printf("DEBUG: Unable to prepare select SQL for UserConfigSetJSON: %s -- error: %v\n", sql, err)
return err
}
defer utility.Close(stmt)
_,err= stmt.Exec()
return err
}

View file

@ -34,6 +34,7 @@ func (p *Persister) AddPage(model models.PageModel) (err error) {
model.Page.SetDefaults() model.Page.SetDefaults()
model.Meta.OrgID = p.Context.OrgID model.Meta.OrgID = p.Context.OrgID
model.Meta.UserID = p.Context.UserID
model.Meta.DocumentID = model.Page.DocumentID model.Meta.DocumentID = model.Page.DocumentID
model.Meta.Created = time.Now().UTC() model.Meta.Created = time.Now().UTC()
model.Meta.Revised = time.Now().UTC() model.Meta.Revised = time.Now().UTC()
@ -65,7 +66,7 @@ func (p *Persister) AddPage(model models.PageModel) (err error) {
err = searches.Add(&databaseRequest{OrgID: p.Context.OrgID}, model.Page, model.Page.RefID) err = searches.Add(&databaseRequest{OrgID: p.Context.OrgID}, model.Page, model.Page.RefID)
stmt2, err := p.Context.Transaction.Preparex("INSERT INTO pagemeta (pageid, orgid, documentid, rawbody, config, externalsource, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") stmt2, err := p.Context.Transaction.Preparex("INSERT INTO pagemeta (pageid, orgid, userid, documentid, rawbody, config, externalsource, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
defer utility.Close(stmt2) defer utility.Close(stmt2)
if err != nil { if err != nil {
@ -73,7 +74,7 @@ func (p *Persister) AddPage(model models.PageModel) (err error) {
return return
} }
_, err = stmt2.Exec(model.Meta.PageID, model.Meta.OrgID, model.Meta.DocumentID, model.Meta.RawBody, model.Meta.Config, model.Meta.ExternalSource, model.Meta.Created, model.Meta.Revised) _, err = stmt2.Exec(model.Meta.PageID, model.Meta.OrgID, model.Meta.UserID, model.Meta.DocumentID, model.Meta.RawBody, model.Meta.Config, model.Meta.ExternalSource, model.Meta.Created, model.Meta.Revised)
if err != nil { if err != nil {
log.Error("Unable to execute insert for page meta", err) log.Error("Unable to execute insert for page meta", err)
@ -291,12 +292,15 @@ func (p *Persister) UpdatePage(page entity.Page, refID, userID string, skipRevis
} }
// UpdatePageMeta persists meta information associated with a document page. // UpdatePageMeta persists meta information associated with a document page.
func (p *Persister) UpdatePageMeta(meta entity.PageMeta) (err error) { func (p *Persister) UpdatePageMeta(meta entity.PageMeta,updateUserID bool) (err error) {
err = nil err = nil
meta.Revised = time.Now().UTC() meta.Revised = time.Now().UTC()
if updateUserID {
meta.UserID=p.Context.UserID
}
var stmt *sqlx.NamedStmt var stmt *sqlx.NamedStmt
stmt, err = p.Context.Transaction.PrepareNamed("UPDATE pagemeta SET documentid=:documentid, rawbody=:rawbody, config=:config, externalsource=:externalsource, revised=:revised WHERE orgid=:orgid AND pageid=:pageid") stmt, err = p.Context.Transaction.PrepareNamed("UPDATE pagemeta SET userid=:userid, documentid=:documentid, rawbody=:rawbody, config=:config, externalsource=:externalsource, revised=:revised WHERE orgid=:orgid AND pageid=:pageid")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -383,7 +387,7 @@ func (p *Persister) DeletePage(documentID, pageID string) (rows int64, err error
func (p *Persister) GetPageMeta(pageID string) (meta entity.PageMeta, err error) { func (p *Persister) GetPageMeta(pageID string) (meta entity.PageMeta, err error) {
err = nil err = nil
stmt, err := Db.Preparex("SELECT id, pageid, orgid, documentid, rawbody, coalesce(config,JSON_UNQUOTE('{}')) as config, externalsource, created, revised FROM pagemeta WHERE orgid=? AND pageid=?") stmt, err := Db.Preparex("SELECT id, pageid, orgid, userid, documentid, rawbody, coalesce(config,JSON_UNQUOTE('{}')) as config, externalsource, created, revised FROM pagemeta WHERE orgid=? AND pageid=?")
defer utility.Close(stmt) defer utility.Close(stmt)
if err != nil { if err != nil {
@ -409,7 +413,7 @@ func (p *Persister) GetDocumentPageMeta(documentID string, externalSourceOnly bo
filter = " AND externalsource=1" filter = " AND externalsource=1"
} }
err = Db.Select(&meta, "SELECT id, pageid, orgid, documentid, rawbody, coalesce(config,JSON_UNQUOTE('{}')) as config, externalsource, created, revised FROM pagemeta WHERE orgid=? AND documentid=?"+filter, p.Context.OrgID, documentID) err = Db.Select(&meta, "SELECT id, pageid, orgid, userid, documentid, rawbody, coalesce(config,JSON_UNQUOTE('{}')) as config, externalsource, created, revised FROM pagemeta WHERE orgid=? AND documentid=?"+filter, p.Context.OrgID, documentID)
if err != nil { if err != nil {
log.Error(fmt.Sprintf("Unable to execute select document page meta for org %s and document %s", p.Context.OrgID, documentID), err) log.Error(fmt.Sprintf("Unable to execute select document page meta for org %s and document %s", p.Context.OrgID, documentID), err)

View file

@ -168,6 +168,7 @@ CREATE TABLE IF NOT EXISTS `pagemeta` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`pageid` CHAR(16) NOT NULL COLLATE utf8_bin, `pageid` CHAR(16) NOT NULL COLLATE utf8_bin,
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin, `orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`userid` CHAR(16) NOT NULL COLLATE utf8_bin,
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin, `documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
`rawbody` LONGBLOB, `rawbody` LONGBLOB,
`config` JSON, `config` JSON,
@ -266,3 +267,16 @@ INSERT INTO `config` VALUES ('LICENSE','{\"token\": \"\",\"endpoint\": \"https:/
INSERT INTO `config` VALUES ('META','{\"database\": \"db_00000.sql\"}'); INSERT INTO `config` VALUES ('META','{\"database\": \"db_00000.sql\"}');
INSERT INTO `config` VALUES ('SECTION-GITHUB', '{\"clientID\": \"\", \"clientSecret\": \"\", \"authorizationCallbackURL\": \"https://localhost:5001/api/public/validate?section=github\"}'); INSERT INTO `config` VALUES ('SECTION-GITHUB', '{\"clientID\": \"\", \"clientSecret\": \"\", \"authorizationCallbackURL\": \"https://localhost:5001/api/public/validate?section=github\"}');
INSERT INTO `config` VALUES ('SECTION-TRELLO','{\"appKey\": \"\"}'); INSERT INTO `config` VALUES ('SECTION-TRELLO','{\"appKey\": \"\"}');
DROP TABLE IF EXISTS `userconfig`;
CREATE TABLE IF NOT EXISTS `userconfig` (
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`userid` CHAR(16) NOT NULL COLLATE utf8_bin,
`key` CHAR(225) NOT NULL,
`config` JSON,
UNIQUE INDEX `idx_userconfig_orguserkey` (`orgid`, `userid`, `key` ASC) )
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-- TODO insert userid into pagemeta table
-- ALTER TABLE `pagemeta` ADD `userid` CHAR(16) NOT NULL COLLATE utf8_bin AFTER `orgid`;

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -40,7 +40,7 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Render converts Gemini data into HTML suitable for browser rendering. // Render converts Gemini data into HTML suitable for browser rendering.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
var items []geminiItem var items []geminiItem
var payload = geminiRender{} var payload = geminiRender{}
var c = geminiConfig{} var c = geminiConfig{}
@ -64,7 +64,7 @@ func (*Provider) Render(config, data string) string {
} }
// Command handles authentication, workspace listing and items retrieval. // Command handles authentication, workspace listing and items retrieval.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
method := query.Get("method") method := query.Get("method")
@ -84,7 +84,7 @@ func (*Provider) Command(w http.ResponseWriter, r *http.Request) {
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) (newData string) { func (*Provider) Refresh(ctx *provider.Context, config, data string) (newData string) {
var c = geminiConfig{} var c = geminiConfig{}
err := json.Unmarshal([]byte(config), &c) err := json.Unmarshal([]byte(config), &c)

View file

@ -14,6 +14,7 @@ package github
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"html/template" "html/template"
"io/ioutil" "io/ioutil"
@ -64,9 +65,22 @@ func authorizationCallbackURL() string {
// NOTE: URL value must have the path and query "/api/public/validate?section=github" // NOTE: URL value must have the path and query "/api/public/validate?section=github"
return request.ConfigString(meta.ConfigHandle(), "authorizationCallbackURL") return request.ConfigString(meta.ConfigHandle(), "authorizationCallbackURL")
} }
func validateToken(ptoken string) error {
// Github authorization check
authClient := gogithub.NewClient((&gogithub.BasicAuthTransport{
Username: clientID(),
Password: clientSecret(),
}).Client())
_, _, err := authClient.Authorizations.Check(clientID(), ptoken)
return err
}
func secretsJSON(token string) string {
return `{"token":"` + strings.TrimSpace(token) + `"}`
}
// Command to run the various functions required... // Command to run the various functions required...
func (p *Provider) Command(w http.ResponseWriter, r *http.Request) { func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
method := query.Get("method") method := query.Get("method")
@ -99,6 +113,25 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
return return
} }
// get the secret token in the database
ptoken := ctx.GetSecrets("token")
switch method {
case "saveSecret": // secret Token update code
// write the new one, direct from JS
if err = ctx.SaveSecrets(string(body)); err != nil {
log.Error("github settoken configuration", err)
provider.WriteError(w, "github", err)
return
}
provider.WriteEmpty(w)
return
}
// load the config from the client-side
config := githubConfig{} config := githubConfig{}
err = json.Unmarshal(body, &config) err = json.Unmarshal(body, &config)
@ -109,17 +142,29 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
} }
config.Clean() config.Clean()
// always use DB version of the token
if len(config.Token) == 0 { config.Token = ptoken
msg := "Missing token"
log.ErrorString("github: " + msg)
provider.WriteMessage(w, "gitub", msg)
return
}
client := p.githubClient(config) client := p.githubClient(config)
switch method { switch method { // the main data handling switch
case "checkAuth":
if len(ptoken) == 0 {
err = errors.New("empty github token")
} else {
err = validateToken(ptoken)
}
if err != nil {
// token now invalid, so wipe it
ctx.SaveSecrets("") // ignore error, already in an error state
log.Error("github check token validation", err)
provider.WriteError(w, "github", err)
return
}
provider.WriteEmpty(w)
return
case tagCommitsData: case tagCommitsData:
@ -208,7 +253,7 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
provider.WriteError(w, "github", err) provider.WriteError(w, "github", err)
return return
} }
for kr, vr := range repos { for _, vr := range repos {
private := "" private := ""
if *vr.Private { if *vr.Private {
private = " (private)" private = " (private)"
@ -216,7 +261,7 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
render = append(render, render = append(render,
githubRepo{ githubRepo{
Name: config.Owner + "/" + *vr.Name + private, Name: config.Owner + "/" + *vr.Name + private,
ID: fmt.Sprintf("%s:%s:%d", config.Owner, *vr.Name, kr), ID: fmt.Sprintf("%s:%s", config.Owner, *vr.Name),
Owner: config.Owner, Owner: config.Owner,
Repo: *vr.Name, Repo: *vr.Name,
Private: *vr.Private, Private: *vr.Private,
@ -229,6 +274,7 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
provider.WriteJSON(w, render) provider.WriteJSON(w, render)
case "branches": case "branches":
if config.Owner == "" || config.Repo == "" { if config.Owner == "" || config.Repo == "" {
provider.WriteJSON(w, []githubBranch{}) // we have nothing to return provider.WriteJSON(w, []githubBranch{}) // we have nothing to return
return return
@ -244,7 +290,7 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
for kc, vb := range branches { for kc, vb := range branches {
render[kc] = githubBranch{ render[kc] = githubBranch{
Name: *vb.Name, Name: *vb.Name,
ID: fmt.Sprintf("%s:%s:%s:%d", config.Owner, config.Repo, *vb.Name, kc), ID: fmt.Sprintf("%s:%s:%s", config.Owner, config.Repo, *vb.Name),
Included: false, Included: false,
URL: "https://github.com/" + config.Owner + "/" + config.Repo + "/tree/" + *vb.Name, URL: "https://github.com/" + config.Owner + "/" + config.Repo + "/tree/" + *vb.Name,
} }
@ -253,6 +299,7 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
provider.WriteJSON(w, render) provider.WriteJSON(w, render)
case "labels": case "labels":
if config.Owner == "" || config.Repo == "" { if config.Owner == "" || config.Repo == "" {
provider.WriteJSON(w, []githubBranch{}) // we have nothing to return provider.WriteJSON(w, []githubBranch{}) // we have nothing to return
return return
@ -277,6 +324,7 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
provider.WriteJSON(w, render) provider.WriteJSON(w, render)
default: default:
log.ErrorString("Github connector unknown method: " + method) log.ErrorString("Github connector unknown method: " + method)
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
@ -573,7 +621,7 @@ func (*Provider) getCommits(client *gogithub.Client, config githubConfig) ([]git
} }
// Refresh ... gets the latest version // Refresh ... gets the latest version
func (p *Provider) Refresh(configJSON, data string) string { func (p *Provider) Refresh(ctx *provider.Context, configJSON, data string) string {
var c = githubConfig{} var c = githubConfig{}
err := json.Unmarshal([]byte(configJSON), &c) err := json.Unmarshal([]byte(configJSON), &c)
@ -584,6 +632,7 @@ func (p *Provider) Refresh(configJSON, data string) string {
} }
c.Clean() c.Clean()
c.Token = ctx.GetSecrets("token")
switch c.ReportInfo.ID { switch c.ReportInfo.ID {
/*case "issuenum_data": /*case "issuenum_data":
@ -634,7 +683,7 @@ func (p *Provider) Refresh(configJSON, data string) string {
} }
// Render ... just returns the data given, suitably formatted // Render ... just returns the data given, suitably formatted
func (p *Provider) Render(config, data string) string { func (p *Provider) Render(ctx *provider.Context, config, data string) string {
var err error var err error
payload := githubRender{} payload := githubRender{}
@ -648,6 +697,8 @@ func (p *Provider) Render(config, data string) string {
} }
c.Clean() c.Clean()
c.Token = ctx.GetSecrets("token")
payload.Config = c payload.Config = c
payload.Repo = c.RepoInfo payload.Repo = c.RepoInfo
payload.Limit = c.BranchLines payload.Limit = c.BranchLines

View file

@ -13,14 +13,13 @@ package github
import ( import (
"html/template" "html/template"
"strings"
"time" "time"
"github.com/documize/community/wordsmith/log" "github.com/documize/community/wordsmith/log"
) )
const tagIssuesData = "issues_data" const tagIssuesData = "issuesData"
const tagCommitsData = "commits_data" const tagCommitsData = "commitsData"
type githubRender struct { type githubRender struct {
Config githubConfig Config githubConfig
@ -218,8 +217,9 @@ type githubIssueActivity struct {
*/ */
type githubConfig struct { type githubConfig struct {
AppKey string `json:"appKey"` // TODO keep? Token string `json:"-"` // NOTE very important that the secret Token is not leaked to the client side, so "-"
Token string `json:"token"` UserID string `json:"userId"`
PageID string `json:"pageId"`
Owner string `json:"owner_name"` Owner string `json:"owner_name"`
Repo string `json:"repo_name"` Repo string `json:"repo_name"`
Branch string `json:"branch"` Branch string `json:"branch"`
@ -239,8 +239,6 @@ type githubConfig struct {
} }
func (c *githubConfig) Clean() { func (c *githubConfig) Clean() {
c.AppKey = strings.TrimSpace(c.AppKey) // TODO keep?
c.Token = strings.TrimSpace(c.Token)
c.Owner = c.OwnerInfo.Name c.Owner = c.OwnerInfo.Name
c.Repo = c.RepoInfo.Repo c.Repo = c.RepoInfo.Repo
for _, l := range c.Lists { for _, l := range c.Lists {

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -36,18 +36,18 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render converts markdown data into HTML suitable for browser rendering. // Render converts markdown data into HTML suitable for browser rendering.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
result := blackfriday.MarkdownCommon([]byte(data)) result := blackfriday.MarkdownCommon([]byte(data))
return string(result) return string(result)
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -61,7 +61,7 @@ type papertrailEvent struct {
} }
type papertrailConfig struct { type papertrailConfig struct {
APIToken string `json:"APIToken"` APIToken string `json:"APIToken"` // only contains the correct token just after it is typed in
Query string `json:"query"` Query string `json:"query"`
Max int `json:"max"` Max int `json:"max"`
Group papertrailOption `json:"group"` Group papertrailOption `json:"group"`

View file

@ -42,7 +42,7 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Render converts Papertrail data into HTML suitable for browser rendering. // Render converts Papertrail data into HTML suitable for browser rendering.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
var search papertrailSearch var search papertrailSearch
var events []papertrailEvent var events []papertrailEvent
var payload = papertrailRender{} var payload = papertrailRender{}
@ -51,6 +51,8 @@ func (*Provider) Render(config, data string) string {
json.Unmarshal([]byte(data), &search) json.Unmarshal([]byte(data), &search)
json.Unmarshal([]byte(config), &c) json.Unmarshal([]byte(config), &c)
c.APIToken = ctx.GetSecrets("APIToken")
max := len(search.Events) max := len(search.Events)
if c.Max < max { if c.Max < max {
max = c.Max max = c.Max
@ -74,7 +76,7 @@ func (*Provider) Render(config, data string) string {
} }
// Command handles authentication, workspace listing and items retrieval. // Command handles authentication, workspace listing and items retrieval.
func (p *Provider) Command(w http.ResponseWriter, r *http.Request) { func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
method := query.Get("method") method := query.Get("method")
@ -101,6 +103,10 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
config.Clean() config.Clean()
if config.APIToken == provider.SecretReplacement {
config.APIToken = ctx.GetSecrets("APIToken")
}
if len(config.APIToken) == 0 { if len(config.APIToken) == 0 {
provider.WriteMessage(w, me, "Missing API token") provider.WriteMessage(w, me, "Missing API token")
return return
@ -108,14 +114,14 @@ func (p *Provider) Command(w http.ResponseWriter, r *http.Request) {
switch method { switch method {
case "auth": case "auth":
auth(config, w, r) auth(ctx, config, w, r)
case "options": case "options":
options(config, w, r) options(config, w, r)
} }
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) (newData string) { func (*Provider) Refresh(ctx *provider.Context, config, data string) (newData string) {
var c = papertrailConfig{} var c = papertrailConfig{}
err := json.Unmarshal([]byte(config), &c) err := json.Unmarshal([]byte(config), &c)
@ -126,6 +132,8 @@ func (*Provider) Refresh(config, data string) (newData string) {
c.Clean() c.Clean()
c.APIToken = ctx.GetSecrets("APIToken")
if len(c.APIToken) == 0 { if len(c.APIToken) == 0 {
log.Error("missing API token", err) log.Error("missing API token", err)
return return
@ -149,7 +157,7 @@ func (*Provider) Refresh(config, data string) (newData string) {
return return
} }
func auth(config papertrailConfig, w http.ResponseWriter, r *http.Request) { func auth(ctx *provider.Context, config papertrailConfig, w http.ResponseWriter, r *http.Request) {
result, err := fetchEvents(config) result, err := fetchEvents(config)
if err != nil { if err != nil {
@ -162,6 +170,8 @@ func auth(config papertrailConfig, w http.ResponseWriter, r *http.Request) {
return return
} }
log.IfErr(ctx.SaveSecrets(`{"APIToken":"` + config.APIToken + `"}`))
provider.WriteJSON(w, result) provider.WriteJSON(w, result)
} }

View file

@ -19,9 +19,14 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/documize/community/documize/api/request"
"github.com/documize/community/wordsmith/log" "github.com/documize/community/wordsmith/log"
) )
// SecretReplacement is a constant used to replace secrets in data-structures when required.
// 8 stars.
const SecretReplacement = "********"
// sectionsMap is where individual sections register themselves. // sectionsMap is where individual sections register themselves.
var sectionsMap = make(map[string]Provider) var sectionsMap = make(map[string]Provider)
@ -43,10 +48,26 @@ func (t *TypeMeta) ConfigHandle() string {
// Provider represents a 'page' in a document. // Provider represents a 'page' in a document.
type Provider interface { type Provider interface {
Meta() TypeMeta // Meta returns section details Meta() TypeMeta // Meta returns section details
Command(w http.ResponseWriter, r *http.Request) // Command is general-purpose method that can return data to UI Command(ctx *Context, w http.ResponseWriter, r *http.Request) // Command is general-purpose method that can return data to UI
Render(config, data string) string // Render converts section data into presentable HTML Render(ctx *Context, config, data string) string // Render converts section data into presentable HTML
Refresh(config, data string) string // Refresh returns latest data Refresh(ctx *Context, config, data string) string // Refresh returns latest data
}
// Context describes the environment the section code runs in
type Context struct {
OrgID string
UserID string
prov Provider
inCommand bool
}
// NewContext is a convenience function.
func NewContext(orgid, userid string) *Context {
if orgid == "" || userid == "" {
log.Error("NewContext incorrect orgid:"+orgid+" userid:"+userid, errors.New("bad section context"))
}
return &Context{OrgID: orgid, UserID: userid}
} }
// Register makes document section type available // Register makes document section type available
@ -71,10 +92,12 @@ func GetSectionMeta() []TypeMeta {
} }
// Command passes parameters to the given section id, the returned bool indicates success. // Command passes parameters to the given section id, the returned bool indicates success.
func Command(section string, w http.ResponseWriter, r *http.Request) bool { func Command(section string, ctx *Context, w http.ResponseWriter, r *http.Request) bool {
s, ok := sectionsMap[section] s, ok := sectionsMap[section]
if ok { if ok {
s.Command(w, r) ctx.prov = s
ctx.inCommand = true
s.Command(ctx, w, r)
} }
return ok return ok
} }
@ -91,19 +114,21 @@ func Callback(section string, w http.ResponseWriter, r *http.Request) error {
} }
// Render runs that operation for the given section id, the returned bool indicates success. // Render runs that operation for the given section id, the returned bool indicates success.
func Render(section, config, data string) (string, bool) { func Render(section string, ctx *Context, config, data string) (string, bool) {
s, ok := sectionsMap[section] s, ok := sectionsMap[section]
if ok { if ok {
return s.Render(config, data), true ctx.prov = s
return s.Render(ctx, config, data), true
} }
return "", false return "", false
} }
// Refresh returns the latest data for a section. // Refresh returns the latest data for a section.
func Refresh(section, config, data string) (string, bool) { func Refresh(section string, ctx *Context, config, data string) (string, bool) {
s, ok := sectionsMap[section] s, ok := sectionsMap[section]
if ok { if ok {
return s.Refresh(config, data), true ctx.prov = s
return s.Refresh(ctx, config, data), true
} }
return "", false return "", false
} }
@ -174,6 +199,28 @@ func WriteForbidden(w http.ResponseWriter) {
log.IfErr(err) log.IfErr(err)
} }
// Secrets handling
// SaveSecrets for the current user/org combination.
// The secrets must be in the form of a JSON format string, for example `{"mysecret":"lover"}`.
// An empty string signifies no valid secrets for this user/org combination.
// Note that this function can only be called within the Command method of a section.
func (c *Context) SaveSecrets(JSONobj string) error {
if !c.inCommand {
return errors.New("SaveSecrets() may only be called from within Command()")
}
return request.UserConfigSetJSON(c.OrgID, c.UserID, c.prov.Meta().ContentType, JSONobj)
}
// GetSecrets for the current context user/org.
// For example (see SaveSecrets example): thisContext.GetSecrets("mysecret")
// JSONpath format is defined at https://dev.mysql.com/doc/refman/5.7/en/json-path-syntax.html .
// An empty JSONpath returns the whole JSON object, as JSON.
// Errors return the empty string.
func (c *Context) GetSecrets(JSONpath string) string {
return request.UserConfigGetJSON(c.OrgID, c.UserID, c.prov.Meta().ContentType, JSONpath)
}
// sort sections in order that that should be presented. // sort sections in order that that should be presented.
type sectionsToSort []TypeMeta type sectionsToSort []TypeMeta

View file

@ -50,7 +50,6 @@ func Register() {
provider.Register("trello", &trello.Provider{}) provider.Register("trello", &trello.Provider{})
provider.Register("wysiwyg", &wysiwyg.Provider{}) provider.Register("wysiwyg", &wysiwyg.Provider{})
provider.Register("zendesk", &zendesk.Provider{}) provider.Register("zendesk", &zendesk.Provider{})
p := provider.List() p := provider.List()
log.Info(fmt.Sprintf("Documize registered %d smart sections", len(p))) log.Info(fmt.Sprintf("Documize registered %d smart sections", len(p)))
} }

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render sends back data as-is (HTML). // Render sends back data as-is (HTML).
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -45,7 +45,7 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
method := query.Get("method") method := query.Get("method")
@ -137,7 +137,7 @@ func (*Provider) Command(w http.ResponseWriter, r *http.Request) {
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
raw := []trelloListCards{} raw := []trelloListCards{}
payload := trelloRender{} payload := trelloRender{}
var c = trelloConfig{} var c = trelloConfig{}
@ -163,7 +163,7 @@ func (*Provider) Render(config, data string) string {
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
var c = trelloConfig{} var c = trelloConfig{}
json.Unmarshal([]byte(config), &c) json.Unmarshal([]byte(config), &c)

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render returns data as-is (HTML). // Render returns data as-is (HTML).
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }

View file

@ -35,16 +35,16 @@ func (*Provider) Meta() provider.TypeMeta {
} }
// Command stub. // Command stub.
func (*Provider) Command(w http.ResponseWriter, r *http.Request) { func (*Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
provider.WriteEmpty(w) provider.WriteEmpty(w)
} }
// Render just sends back HMTL as-is. // Render just sends back HMTL as-is.
func (*Provider) Render(config, data string) string { func (*Provider) Render(ctx *provider.Context, config, data string) string {
return data return data
} }
// Refresh just sends back data as-is. // Refresh just sends back data as-is.
func (*Provider) Refresh(config, data string) string { func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
return data return data
} }