diff --git a/.gitignore b/.gitignore
index f2515656..8dec467e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,7 +93,7 @@ local.properties
# PDT-specific
.buildpath
-# sbteclipse plugin
+# sbteclipse plugin
.target
# TeXlipse plugin
@@ -141,3 +141,8 @@ build/Release
# Destination folder
/www/
+
+.idea
+
+.idea/workspace.xml
+
diff --git a/src/js/controllers/project.js b/src/js/controllers/project.js
index 7aa0c4f1..b57a1e5f 100644
--- a/src/js/controllers/project.js
+++ b/src/js/controllers/project.js
@@ -53,6 +53,14 @@ angular.module('Teem')
redirectTo: function(params) {
return '/teems/' + params.id + '?tab=' + params.tab;
}
+ })
+ .when('/trello/get/',{
+ controller: 'TrelloGetController',
+ template: '
Redirecting ....
'
+ })
+ .when('/trello/auth',{
+ template: 'Redirecting to trello
',
+ controller: 'TrelloAuthController'
});
}])
.controller('FetchProject', [
@@ -255,7 +263,7 @@ angular.module('Teem')
$scope.pad.editing = false;
});
- $scope.$on('$routeChangeStart', function(event, next, current) {
+ $scope.$on('$routeChangeStart', function(event, next, current) {
if (current.params.tab !== undefined && $scope.project!== undefined) {
$scope.project.setTimestampAccess(current.params.tab);
}
@@ -281,12 +289,12 @@ angular.module('Teem')
var lastChange = $scope.project.lastChange(section);
var lastAccess;
- if ($scope.project.getTimestampAccess() &&
- $scope.project.getTimestampAccess()[section]) {
- lastAccess = new Date(($scope.project.getTimestampAccess()[section]).last);
- } else {
- lastAccess = new Date(0);
- }
+ if ($scope.project.getTimestampAccess() &&
+ $scope.project.getTimestampAccess()[section]) {
+ lastAccess = new Date(($scope.project.getTimestampAccess()[section]).last);
+ } else {
+ lastAccess = new Date(0);
+ }
return lastChange > lastAccess;
};
@@ -361,7 +369,7 @@ angular.module('Teem')
url
}).then(function (response) {
angular.forEach(response.data.geonames, function(v) {
- v.value = {
+ v.value = {
id: v.geonameId.toString(),
latitide: v.lat,
longitude: v.lng,
@@ -422,6 +430,16 @@ angular.module('Teem')
};
}])
+ .controller('TrelloGetController', ['$location','ProjectsSvc','SessionSvc', function($location,ProjectsSvc,SessionSvc){
+ let token = $location.hash().split('=')[1];
+ localStorage.setItem('trelloTeemToken',token);
+ SessionSvc.onLoad(function(){
+ ProjectsSvc.updateTrello();
+ });
+ }])
+ .controller('TrelloAuthController',['trelloSvc',function(trelloSvc){
+ trelloSvc.getToken();
+ }])
.directive(
'hideTabs',
function (SharedState, $timeout) {
diff --git a/src/js/directives/pad.js b/src/js/directives/pad.js
index 8133d0f1..15b327ac 100644
--- a/src/js/directives/pad.js
+++ b/src/js/directives/pad.js
@@ -16,33 +16,119 @@ angular.module('Teem')
$scope.editingDefault = attrs.editingDefault;
},
controller: [
- 'SessionSvc', '$rootScope', '$scope', '$route', '$location',
- '$timeout', 'SharedState', 'needWidget', '$element',
- function(SessionSvc, $rootScope, $scope, $route, $location,
- $timeout, SharedState, needWidget, $element) {
-
- var buttons = ['text_fields', 'format_bold', 'format_italic', 'format_strikethrough',
- 'format_align_left', 'format_align_center', 'format_align_right',
- 'format_list_bulleted', 'format_list_numbered'];
-
- var annotationMap = {
- 'text_fields': 'paragraph/header=h3',
- 'format_bold': 'style/fontWeight=bold',
- 'format_italic': 'style/fontStyle=italic',
- 'format_strikethrough': 'style/textDecoration=line-through',
- 'format_align_left': 'paragraph/textAlign=left',
- 'format_align_center': 'paragraph/textAlign=center',
- 'format_align_right': 'paragraph/textAlign=right',
- 'format_list_bulleted': 'paragraph/listStyleType=unordered',
- 'format_list_numbered': 'paragraph/listStyleType=decimal'
- };
-
- var annotations = {};
-
- function imgWidget(parentElement, before, state) {
- state = state || before;
-
- if (!(state in $scope.project.attachments) || !$scope.project.attachments[state].file) {
+ 'SessionSvc', '$rootScope', '$scope', '$route', '$location',
+ '$timeout', 'SharedState', 'needWidget', '$element','linkPreview',
+ function(SessionSvc, $rootScope, $scope, $route, $location,
+ $timeout, SharedState, needWidget, $element, linkPreview) {
+
+ var buttons = ['text_fields', 'format_bold', 'format_italic', 'format_strikethrough',
+ 'format_align_left', 'format_align_center', 'format_align_right',
+ 'format_list_bulleted', 'format_list_numbered'];
+
+ var annotationMap = {
+ 'text_fields': 'paragraph/header=h3',
+ 'format_bold': 'style/fontWeight=bold',
+ 'format_italic': 'style/fontStyle=italic',
+ 'format_strikethrough': 'style/textDecoration=line-through',
+ 'format_align_left': 'paragraph/textAlign=left',
+ 'format_align_center': 'paragraph/textAlign=center',
+ 'format_align_right': 'paragraph/textAlign=right',
+ 'format_list_bulleted': 'paragraph/listStyleType=unordered',
+ 'format_list_numbered': 'paragraph/listStyleType=decimal'
+ };
+
+ var annotations = {};
+
+ function openLinkPopover(event,range){
+ if(!styleAppended){
+ let pStyle = document.createElement('style');
+ pStyle.innerHTML = `
+ #popover{
+ width: 330px;
+ height: 270px;
+ margin: 0 5px;
+ border-radius: 6px;
+ }
+ div.popover-link-image{
+ width: 330px;
+ height: 190px;
+ margin: 5px auto;
+ }
+ div.popover-link-description{
+ width: 320px;
+ height: auto;
+ max-height: 40px;
+ margin: 0 auto;
+ overflow: auto;
+ word-wrap: break-all;
+ }
+ .popover-link-title{
+ word-wrap: break-all;
+ text-overflow: ellpsis;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+ .popover-link-address{
+ color: #000;
+ margin-left: 5px;
+ overflow: auto;
+ word-wrap: break-all;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .popover-link-title{
+ margin-left: 5px;
+ }
+ #popover-container:after{
+ content: "";
+ position: absolute;
+ bottom: -25px;
+ left: 175px;
+ border-style: solid;
+ visibility: hidden;
+ width: 0;
+ z-index: 1;
+ }
+ #popover-container:before{
+ content: "";
+ position: absolute;
+ top: -11px;
+ left: -1px;
+ border-style: solid;
+ border-width: 0 10px 10px;
+ border-color: #F1F1F1 transparent;
+ display: block;
+ width: 0;
+ z-index: 0;
+ }`;
+ document.body.appendChild(pStyle);
+ styleAppended = true;
+ }
+ timer = $timeout(() => {
+ event.stopPropagation();
+ let div = document.createElement('div');
+ let btn = event.target;
+ console.dir(btn.offsetHeight);
+ let inHTML = `
+
+
+ `;
+ linkPreview.getMetaData(btn.href)
+ .then((meta) => {
+ console.log(meta);
+ if(!meta){
+ div.style.display = 'none';
return;
}
@@ -54,7 +140,89 @@ angular.module('Teem')
- `;
+ ${urlDescription}
+ ${btn.href}
+ `;
+ }
+ else if(urlDescription && !urlImage){
+ div.style.height = '110px';
+ inHTML = `
+
+
${urlDescription}
+
${btn.href}
+
`;
+ }
+ else{
+ if(!urlTitle){
+ div.style.height = '110px';
+ inHTML = `
+
No Description provided ...
+
${btn.href}
+
`;
+ }
+ div.style.height = '110px';
+ inHTML = `
+
${urlTitle}
+
No Description provided ...
+
${btn.href}
+
`;
+ }
+ div.innerHTML = inHTML;
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ let clientRect = range.node.nextSibling ?
+ range.node.nextSibling.getBoundingClientRect() :
+ range.node.parentElement.getBoundingClientRect();
+ div.innerHTML = inHTML;
+ div.style.width = '345px';
+ div.style.height = '300px';
+ div.style.position = 'absolute';
+ div.style.border = '1px solid #F0F0F0';
+ div.style.top = clientRect.top + 35 + 'px';
+ div.style.left = clientRect.left + 'px';
+ div.style.zIndex = 3;
+ div.style.backgroundColor = '#F2F2F2';
+ div.style.paddingTop = '5px';
+ div.id = 'popover-container';
+ document.body.appendChild(div);
+ },700);
+ }
+
+ function closeLinkPopover(delay){
+ if(timer){
+ $timeout.cancel(timer);
+ timer = null;
+ $timeout(() => {
+ if(document.getElementById('popover-container')){
+ document.body.removeChild(document.getElementById('popover-container'));
+ }
+ }, delay);
+ }
+ }
+
+ function imgWidget(parentElement, before, state) {
+ state = state || before;
+
+ if (!(state in $scope.project.attachments) || !$scope.project.attachments[state].file) {
+ return;
+ }
+
+ // cannot use spinner template directly here
+ parentElement.innerHTML = `
+ `;
+
+ $scope.project.attachments[state].file.getUrl().then(url => {
+ parentElement.innerHTML = `
`;
+ });
+ }
$scope.project.attachments[state].file.getUrl().then(url => {
parentElement.innerHTML = `
`;
diff --git a/src/js/services/pad/need-widget.js b/src/js/services/pad/need-widget.js
index e75568fa..b6e10dff 100644
--- a/src/js/services/pad/need-widget.js
+++ b/src/js/services/pad/need-widget.js
@@ -58,7 +58,9 @@ angular.module('Teem')
editor.deleteText(selection);
}
- need = scope.project.addNeed(need);
+ need = scope.project.addNeedWithoutTrello(need);
+ //FIXME Doesn't work with the Trello integration, for now the ones created in pad cannot be added to Trello
+ // need = scope.project.addNeed(need);
// To generate need added event after all the info is available
diff --git a/src/js/services/projectsService.js b/src/js/services/projectsService.js
index bff4ab11..d52cb3b5 100644
--- a/src/js/services/projectsService.js
+++ b/src/js/services/projectsService.js
@@ -2,616 +2,683 @@
angular.module('Teem')
.factory('ProjectsSvc', [
- 'swellRT', '$q', '$timeout', 'base64', 'SessionSvc', 'SwellRTCommon', 'User',
- '$rootScope', 'Logo', 'Url', 'Participation',
- function(swellRT, $q, $timeout, base64, SessionSvc, SwellRTCommon, User,
- $rootScope, Logo, Url, Participation){
-
- // class that expose only read methods of the project object
- class ProjectReadOnly extends aggregation(Object, Logo, Url, Participation.ReadOnly) {
-
- // val is a readOnly object from a SwellRT query result
- constructor (val) {
- // calling "this" is not allowed before super()
- super();
-
- if (val) {
- for (var k in val.root){
- if (val.root.hasOwnProperty(k)){
- this[k] = val.root[k];
+ 'swellRT', '$q', '$timeout', 'base64', 'SessionSvc', 'SwellRTCommon', 'User',
+ '$rootScope', 'Logo', 'Url', 'Participation', 'trelloSvc', '$location',
+ function (swellRT, $q, $timeout, base64, SessionSvc, SwellRTCommon, User,
+ $rootScope, Logo, Url, Participation, trelloSvc, $location) {
+
+ // class that expose only read methods of the project object
+ class ProjectReadOnly extends aggregation(Object, Logo, Url, Participation.ReadOnly) {
+
+ // val is a readOnly object from a SwellRT query result
+ constructor(val) {
+ // calling "this" is not allowed before super()
+ super();
+
+ if (val) {
+ for (var k in val.root) {
+ if (val.root.hasOwnProperty(k)) {
+ this[k] = val.root[k];
+ }
}
- }
- this._participants = val.participants;
- if (val._id && val._id.$oid){
- this._creationDate =
- parseInt(val._id.$oid.slice(0, 8), 16) * 1000;
+ this._participants = val.participants;
+ if (val._id && val._id.$oid) {
+ this._creationDate =
+ parseInt(val._id.$oid.slice(0, 8), 16) * 1000;
} else {
this._creationDate = 0;
}
+ }
}
- }
- get pathPrefix () { return '/teems/'; }
-
- getTimestampAccess () {
- var access;
-
- if (this.lastAccesses === undefined) {
- this.lastAccesses = [];
+ get pathPrefix() {
+ return '/teems/';
}
- angular.forEach(this.lastAccesses, function(a) {
- if (a.user === User.currentId()) {
- access = a;
- }
- });
+ getTimestampAccess() {
+ var access;
- return access;
- }
+ if (this.lastAccesses === undefined) {
+ this.lastAccesses = [];
+ }
- // section in {'chat', 'pad', 'needs'}
- lastChange (section) {
- if (section === undefined) {
- //cast to Date
- return new Date(Math.max(
- this.lastChange('chat').getTime(),
- this.lastChange('pad').getTime(),
- this.lastChange('needs').getTime()
- ));
- } else {
- switch (section) {
-
- case 'chat':
- if (this.chat && this.chat.length > 0) {
- return new Date(this.chat[this.chat.length -1].time);
- } else {
- return new Date(0);
+ angular.forEach(this.lastAccesses, function (a) {
+ if (a.user === User.currentId()) {
+ access = a;
}
- break;
-
- case 'pad':
- return new Date(this.pad.lastmodtime) || new Date(0);
-
- case 'needs':
- var lastChange = new Date(0);
- angular.forEach(this.needs, function(n){
- lastChange = Math.max(
- lastChange,
- new Date(n.time||0),
- (function() {
- var lastComment = new Date(0);
- angular.forEach(n.comments, function(c){
- lastComment = Math.max(lastComment, new Date(c.time || 0));
- });
- return lastComment;
- }())
- );
- });
- // cast to Date
- return new Date(lastChange);
+ });
+
+ return access;
+ }
- default:
- return new Date(0);
+ // section in {'chat', 'pad', 'needs'}
+ lastChange(section) {
+ if (section === undefined) {
+ //cast to Date
+ return new Date(Math.max(
+ this.lastChange('chat').getTime(),
+ this.lastChange('pad').getTime(),
+ this.lastChange('needs').getTime()
+ ));
+ } else {
+ switch (section) {
+
+ case 'chat':
+ if (this.chat && this.chat.length > 0) {
+ return new Date(this.chat[this.chat.length - 1].time);
+ } else {
+ return new Date(0);
+ }
+ break;
+
+ case 'pad':
+ return new Date(this.pad.lastmodtime) || new Date(0);
+
+ case 'needs':
+ var lastChange = new Date(0);
+ angular.forEach(this.needs, function (n) {
+ lastChange = Math.max(
+ lastChange,
+ new Date(n.time || 0),
+ (function () {
+ var lastComment = new Date(0);
+ angular.forEach(n.comments, function (c) {
+ lastComment = Math.max(lastComment, new Date(c.time || 0));
+ });
+ return lastComment;
+ }())
+ );
+ });
+ // cast to Date
+ return new Date(lastChange);
+
+ default:
+ return new Date(0);
+ }
}
}
- }
- get creationDate(){
- return this._creationDate;
- }
+ get creationDate() {
+ return this._creationDate;
+ }
- // section in {'chat', 'pad', 'needs'}
- // pos in {'last','prev'}
- lastAccess (section, pos) {
- var access;
+ // section in {'chat', 'pad', 'needs'}
+ // pos in {'last','prev'}
+ lastAccess(section, pos) {
+ var access;
- angular.forEach(this.lastAccesses || [], function(a) {
- if (a.user === User.currentId()) {
- access = a;
+ angular.forEach(this.lastAccesses || [], function (a) {
+ if (a.user === User.currentId()) {
+ access = a;
+ }
+ });
+
+ if (!pos) {
+ pos = 'last';
}
- });
- if (!pos) {
- pos = 'last';
+ return (access && access[section] && access[section][pos] ? new Date(access[section][pos]) : new Date(0));
}
- return (access && access[section] && access[section][pos] ? new Date(access[section][pos]) : new Date(0));
- }
-
- newMessagesCount () {
- var access = this.lastAccess('chat');
+ newMessagesCount() {
+ var access = this.lastAccess('chat');
- if (this.chat.length > 0){
- var i = this.chat.length - 1;
+ if (this.chat.length > 0) {
+ var i = this.chat.length - 1;
- while (i > -1 && (new Date(this.chat[i].time) > access)) {
- i --;
+ while (i > -1 && (new Date(this.chat[i].time) > access)) {
+ i--;
+ }
+ return this.chat.length - 1 - i;
+ } else {
+ return 0;
}
- return this.chat.length - 1 - i;
- } else {
- return 0;
}
- }
- padEditionCount () {
- if (this.lastAccess('pad').getTime() < this.pad.lastmodtime) {
- return 1;
- } else {
- return 0;
+ padEditionCount() {
+ if (this.lastAccess('pad').getTime() < this.pad.lastmodtime) {
+ return 1;
+ } else {
+ return 0;
+ }
}
- }
- isFeatured () {
- return this.featured !== undefined && this.featured !== 'false';
- }
+ isFeatured() {
+ return this.featured !== undefined && this.featured !== 'false';
+ }
- featureDate () {
- if (this.isFeatured()){
- return parseInt(this.featured)||0;
- } else {
- return -1;
+ featureDate() {
+ if (this.isFeatured()) {
+ return parseInt(this.featured) || 0;
+ } else {
+ return -1;
+ }
}
- }
- isSupporter (userId = User.currentId()) {
- if (!userId) {
- return false;
+ isSupporter(userId = User.currentId()) {
+ if (!userId) {
+ return false;
+ }
+
+ return this.supporters.indexOf(userId) > -1;
}
- return this.supporters.indexOf(userId) > -1;
- }
+ needCompletedCount() {
+ var completed = 0;
- needCompletedCount () {
- var completed = 0;
+ angular.forEach(this.needs, function (need) {
+ if (need.completed === 'true') {
+ completed++;
+ }
+ });
- angular.forEach(this.needs, function(need) {
- if (need.completed === 'true') {
- completed++;
- }
- });
+ return completed;
+ }
- return completed;
- }
+ needIncompletedCount() {
+ return this.needCount() - this.needCompletedCount();
+ }
- needIncompletedCount () {
- return this.needCount() - this.needCompletedCount();
- }
+ needCount() {
+ if (this.needs === undefined) {
+ return 0;
+ }
- needCount () {
- if (this.needs === undefined) {
- return 0;
+ return this.needs.length;
}
- return this.needs.length;
- }
+ progressPercentage() {
+ var size = this.needCount();
- progressPercentage () {
- var size = this.needCount();
+ if (size === 0) {
+ return 0;
+ }
- if (size === 0) {
- return 0;
+ return Math.round(this.needCompletedCount() * 100 / size);
}
- return Math.round(this.needCompletedCount() * 100 / size);
- }
+ // Show at least 1%
+ progressPercentageNotZero() {
+ var value = this.progressPercentage();
- // Show at least 1%
- progressPercentageNotZero () {
- var value = this.progressPercentage();
+ if (value === 0 && this.needCount() > 0) {
+ return 1;
+ }
- if (value === 0 && this.needCount() > 0) {
- return 1;
+ return value;
}
- return value;
- }
+ progressType() {
+ var percentage = this.progressPercentage();
- progressType () {
- var percentage = this.progressPercentage();
+ if (percentage < 33) {
+ return 'danger';
+ } else if (percentage > 66) {
+ return 'success';
+ } else {
+ return 'warning';
+ }
+ }
- if (percentage < 33) {
- return 'danger';
- } else if (percentage > 66) {
- return 'success';
- } else {
- return 'warning';
+ isShareMode(mode) {
+ return mode === this.shareMode;
}
}
- isShareMode (mode) {
- return mode === this.shareMode;
- }
- }
+ class Project extends aggregation(ProjectReadOnly, Participation.ReadWrite, SessionSvc.SynchedModel) {
- class Project extends aggregation(ProjectReadOnly, Participation.ReadWrite, SessionSvc.SynchedModel) {
+ setShareMode(shareMode) {
+ this.shareMode = shareMode;
+ }
- setShareMode (shareMode) {
- this.shareMode = shareMode;
- }
+ /*
+ * Record when the user had her last and previous access to one project section
+ */
+ setTimestampAccess(section, overridePrev) {
+ if (!this.isParticipant()) {
+ return;
+ }
- /*
- * Record when the user had her last and previous access to one project section
- */
- setTimestampAccess (section, overridePrev) {
- if (! this.isParticipant()) {
- return;
- }
+ if (this.getTimestampAccess() === undefined) {
+ this.lastAccesses.push({user: User.currentId()});
+ }
+ var timestamp = this.getTimestampAccess()[section];
- if (this.getTimestampAccess() === undefined){
- this.lastAccesses.push({user: User.currentId()});
- }
- var timestamp = this.getTimestampAccess()[section];
+ if (timestamp === undefined) {
+ timestamp = {};
+ }
- if (timestamp === undefined) {
- timestamp = {};
- }
+ if (timestamp instanceof Date) {
+ timestamp = {
+ last: timestamp
+ };
+ }
- if (timestamp instanceof Date) {
- timestamp = {
- last: timestamp
- };
- }
+ if (!overridePrev) {
+ timestamp.prev = timestamp.last || (new Date()).toJSON();
+ } else {
+ timestamp.prev = (new Date()).toJSON();
+ }
- if(! overridePrev){
- timestamp.prev = timestamp.last || (new Date()).toJSON();
- } else {
- timestamp.prev = (new Date()).toJSON();
- }
+ timestamp.last = (new Date()).toJSON();
- timestamp.last = (new Date()).toJSON();
+ this.getTimestampAccess()[section] = timestamp;
+ }
- this.getTimestampAccess()[section] = timestamp;
- }
+ toggleSupport() {
- toggleSupport () {
+ var userId = User.currentId();
- var userId = User.currentId();
+ if (userId === undefined) {
+ return;
+ }
+ var index = this.supporters.indexOf(userId);
- if (userId === undefined) {
- return;
+ if (index > -1) {
+ this.supporters.splice(index, 1);
+ } else {
+ this.supporters.push(userId);
+ }
}
- var index = this.supporters.indexOf(userId);
- if (index > -1) {
- this.supporters.splice(index, 1);
- } else {
- this.supporters.push(userId);
+ addAttachment(file) {
+ if (!file) {
+ return;
+ }
+ if (typeof this.attachments === 'undefined') {
+ this.attachments = {};
+ }
+ var swellFile = new swellRT.FileObject(file);
+ var id = Math.floor((1 + Math.random()) * 0x100000000000000).toString(16);
+ this.attachments[id] = {
+ id,
+ file: swellFile,
+ who: User.currentId(),
+ time: (new Date()).toJSON()
+ };
+ return id;
}
- }
- addAttachment (file) {
- if (!file) {
- return;
- }
- if (typeof this.attachments === 'undefined') {
- this.attachments = {};
+ addChatMessage(text, file) {
+ if (file) {
+ file = new swellRT.FileObject(file);
+ }
+ this.chat.push({
+ text,
+ file,
+ who: User.currentId(),
+ time: (new Date()).toJSON()
+ });
+
+ this.setTimestampAccess('chat', true);
}
- var swellFile = new swellRT.FileObject(file);
- var id = Math.floor((1+Math.random())*0x100000000000000).toString(16);
- this.attachments[id] = {
- id,
- file: swellFile,
- who: User.currentId(),
- time: (new Date()).toJSON()
- };
- return id;
- }
- addChatMessage (text, file) {
- if (file) {
- file = new swellRT.FileObject(file);
+ findNeed(id) {
+ return this.needs.filter(function (need) {
+ return need._id === id;
+ })[0];
}
- this.chat.push({
- text,
- file,
- who: User.currentId(),
- time: (new Date()).toJSON()
- });
- this.setTimestampAccess('chat', true);
- }
+ addNeed(need) {
+ // Quick dirty hack until SwellRT provides ids for array elements
+ need._id = Math.random().toString().substring(2);
+ need.author = SessionSvc.users.current();
+ need.time = (new Date()).toJSON();
+ need.completed = 'false';
- findNeed (id) {
- return this.needs.filter(function (need) {
- return need._id === id;
- })[0];
- }
+ this.needs.push(need);
+ this.setTimestampAccess('needs', true);
- addNeed(need) {
+ if (need.text === '') {
+ return need;
+ }
+ if (this.trello) {
+ if (this.trello.boardId && this.trello.listId) {
+ trelloSvc.addNewCard(this.trello, need).then((cardData) => {
+ need.trelloId = cardData.id;
+ this.needs.push(need);
+ })
+ .catch(err => console.log(err));
+ }
+ }
+ else {
+ this.needs.push(need);
+ }
- // Quick dirty hack until SwellRT provides ids for array elements
- need._id = Math.random().toString().substring(2);
- need.author = SessionSvc.users.current();
- need.time = (new Date()).toJSON();
- need.completed = 'false';
+ return need;
+ }
- this.needs.push(need);
- this.setTimestampAccess('needs', true);
+ addNeedWithoutTrello(need) {
+ need._id = Math.random().toString().substring(2);
+ need.author = SessionSvc.users.current();
+ need.time = (new Date()).toJSON();
+ need.completed = 'false';
+ this.setTimestampAccess('needs', true);
+ this.needs.push(need);
- return need;
- }
+ return need;
+ }
- toggleNeedCompleted (need) {
- var newStatus;
+ toggleNeedCompleted(need) {
+ var newStatus;
- if (! this.isParticipant()) {
- return;
- }
+ if (!this.isParticipant()) {
+ return;
+ }
- newStatus = need.completed !== 'true';
+ newStatus = need.completed !== 'true';
- need.completed = newStatus.toString();
+ need.completed = newStatus.toString();
- if (newStatus) {
- need.completionDate = (new Date()).toJSON();
- } else {
- delete need.completionDate;
+ if (newStatus) {
+ need.completionDate = (new Date()).toJSON();
+ trelloSvc.archiveCard(this.trello, need).then(data => need.closed = true)
+ .catch(err => console.log(err));
+ } else {
+ trelloSvc.unarchiveCard(this.trello, need).then(data => {
+ need.closed = false;
+ })
+ .catch(err => console.log(err));
+ delete need.completionDate;
+ }
}
- }
- toggleFeatured () {
- if (! this.isParticipant()) {
- return;
- }
+ toggleFeatured() {
+ if (!this.isParticipant()) {
+ return;
+ }
- if (this.isFeatured()){
- this.featured = 'false';
- } else {
- this.featured = new Date().getTime().toString();
+ if (this.isFeatured()) {
+ this.featured = 'false';
+ } else {
+ this.featured = new Date().getTime().toString();
+ }
}
- }
- removeNeed (need) {
- var i = this.needs.indexOf(need);
+ removeNeed(need) {
+ var i = this.needs.indexOf(need);
- this.needs.splice(i,1);
- }
+ this.needs.splice(i, 1);
- addNeedComment (need, comment) {
- if (!need.comments){
- need.comments = [];
+ trelloSvc.archiveNeed(this.trello, need).then(data => need.removed = true)
+ .catch(err => console.log(err));
}
- need.comments.push({
- text: comment,
- time: (new Date()).toJSON(),
- author: User.currentId()
- });
- this.setTimestampAccess('needs', true);
- }
- delete () {
- this.type = 'deleted';
- this.communities = [];
- }
- }
+ addNeedComment(need, comment) {
+ if (!need.comments) {
+ need.comments = [];
+ }
+ if (this.trello) {
+ trelloSvc.addNewComment(this.trello, need, comment).then(data => console.log(data))
+ .catch(err => console.log(err));
+ }
+ need.comments.push({
+ text: comment,
+ time: (new Date()).toJSON(),
+ author: User.currentId()
+ });
+ this.setTimestampAccess('needs', true);
+ }
- // Service functions //
+ delete() {
+ this.type = 'deleted';
+ this.communities = [];
+ }
+ }
- var openedProjects = {};
+ // Service functions //
+ var openedProjects = {};
- /*
- * Build options for all query
- */
- function buildAllQuery(options) {
- var query = {
- _aggregate: [
- {
- $match: {
- 'root.type': 'project',
- 'root.title': {$ne: ''}
+ /*
+ * Build options for all query
+ */
+ function buildAllQuery(options) {
+ var query = {
+ _aggregate: [
+ {
+ $match: {
+ 'root.type': 'project',
+ 'root.title': {$ne: ''}
+ }
+ },
+ {
+ $sort: {'root.pad.lastmodtime': -1}
+ },
+ {
+ $skip: options.pagination.pageIndex * options.pagination.pageSize
+ },
+ {
+ $limit: options.pagination.pageSize
}
- },
- {
- $sort : {'root.pad.lastmodtime': -1 }
- },
- {
- $skip: options.pagination.pageIndex * options.pagination.pageSize
- },
- {
- $limit: options.pagination.pageSize
- }
- ]
- };
+ ]
+ };
- // all method only list public and user's projects
- query._aggregate[0].$match.$or = [
- // when anonymous user, Users.currentId is undefined
- { 'participants': User.currentId() || 'anonymous' },
- { 'root.shareMode': 'public' }
- ];
+ // all method only list public and user's projects
+ query._aggregate[0].$match.$or = [
+ // when anonymous user, Users.currentId is undefined
+ {'participants': User.currentId() || 'anonymous'},
+ {'root.shareMode': 'public'}
+ ];
- if (options.contributor) {
- query._aggregate[0].$match.participants = options.contributor;
- }
+ if (options.contributor) {
+ query._aggregate[0].$match.participants = options.contributor;
+ }
- if (options.community) {
- query._aggregate[0].$match['root.communities'] = options.community;
- }
+ if (options.community) {
+ query._aggregate[0].$match['root.communities'] = options.community;
+ }
- if (options.localId) {
- query._aggregate[0].$match['root.localId'] = options.localId;
- }
+ if (options.localId) {
+ query._aggregate[0].$match['root.localId'] = options.localId;
+ }
- if (options.shareMode) {
- query._aggregate[0].$match['root.shareMode'] = 'public';
- }
+ if (options.shareMode) {
+ query._aggregate[0].$match['root.shareMode'] = 'public';
+ }
- if (options.titleLike) {
- query._aggregate[0].$match['root.title'] = {
- $regex: options.titleLike,
- $options: 'i'
- };
- }
+ if (options.titleLike) {
+ query._aggregate[0].$match['root.title'] = {
+ $regex: options.titleLike,
+ $options: 'i'
+ };
+ }
- if (options.featured) {
- query._aggregate[0].$match['root.featured'] = {$exists: true, $ne: 'false'};
- query._aggregate[1].$sort = {'root.featured' : -1};
- }
+ if (options.featured) {
+ query._aggregate[0].$match['root.featured'] = {$exists: true, $ne: 'false'};
+ query._aggregate[1].$sort = {'root.featured': -1};
+ }
- if (options.latest) {
- query._aggregate[1].$sort = {'_id' : -1};
- }
+ if (options.latest) {
+ query._aggregate[1].$sort = {'_id': -1};
+ }
- if (options.projection){
- query._aggregate.push({
- $project: options.projection
- });
- }
+ if (options.projection) {
+ query._aggregate.push({
+ $project: options.projection
+ });
+ }
- return query;
- }
+ return query;
+ }
- /*
- * Find all the projects that meet some condition
- */
- function all(options) {
+ /*
+ * Find all the projects that meet some condition
+ */
+ function all(options) {
- if (!options.pagination) {
- options.pagination = {};
- }
+ if (!options.pagination) {
+ options.pagination = {};
+ }
- if (!options.pagination.pageSize) {
- options.pagination.pageSize = 12;
- }
+ if (!options.pagination.pageSize) {
+ options.pagination.pageSize = 12;
+ }
- if (!options.pagination.pageIndex) {
- options.pagination.pageIndex = 0;
- }
+ if (!options.pagination.pageIndex) {
+ options.pagination.pageIndex = 0;
+ }
- var projects = [],
- query = buildAllQuery(options);
+ var projects = [],
+ query = buildAllQuery(options);
- var nextPage = function () {
- // build a new options parameter for next page
- var nextPageOptions = options;
+ var nextPage = function () {
+ // build a new options parameter for next page
+ var nextPageOptions = options;
- nextPageOptions.pagination.pageIndex += 1;
+ nextPageOptions.pagination.pageIndex += 1;
- return all(nextPageOptions);
- };
+ return all(nextPageOptions);
+ };
- var projsPromise = $q(function(resolve, reject) {
- swellRT.query(query, function(result) {
+ var projsPromise = $q(function (resolve, reject) {
+ swellRT.query(query, function (result) {
- if (result.length === 0) {
- nextPage = undefined;
- }
+ if (result.length === 0) {
+ nextPage = undefined;
+ }
- angular.forEach(result.result, function(val){
- var v = new ProjectReadOnly(val);
- projects.push(v);
- });
+ angular.forEach(result.result, function (val) {
+ var v = new ProjectReadOnly(val);
+ projects.push(v);
+ });
- resolve(projects);
- },
- function(error){
- console.log(error);
+ resolve(projects);
+ },
+ function (error) {
+ console.log(error);
- reject([]);
+ reject([]);
+ });
});
- });
- projsPromise.next = nextPage;
+ projsPromise.next = nextPage;
- return projsPromise;
- }
+ return projsPromise;
+ }
- function contributing (options = {}) {
+ function contributing(options = {}) {
- if (!SessionSvc.users.loggedIn()) {
- return $q(function(resolve) {
- resolve([]);
- });
- }
+ if (!SessionSvc.users.loggedIn()) {
+ return $q(function (resolve) {
+ resolve([]);
+ });
+ }
- options.contributor = User.currentId();
+ options.contributor = User.currentId();
- return all(options);
- }
+ return all(options);
+ }
- function find(id) {
- var def = $q.defer();
+ function find(id) {
+ var def = $q.defer();
- if (!openedProjects[id]) {
- openedProjects[id] = def.promise;
- swellRT.openModel(id, function(model){
- $timeout(function(){
- var pr = swellRT.proxy(model, Project);
- def.resolve(pr);
+ if (!openedProjects[id]) {
+ openedProjects[id] = def.promise;
+ swellRT.openModel(id, function (model) {
+ $timeout(function () {
+ var pr = swellRT.proxy(model, Project);
+ def.resolve(pr);
+ });
+ }, function (error) {
+ console.log(error);
+ def.reject(error);
});
- }, function(error){
- console.log(error);
- def.reject(error);
- });
+ }
+ return openedProjects[id];
}
- return openedProjects[id];
- }
- function findByUrlId(urlId) {
- return find(base64.urldecode(urlId));
- }
+ function findByUrlId(urlId) {
+ return find(base64.urldecode(urlId));
+ }
- function create(options, callback) {
- var d = $q.defer();
+ function create(options, callback) {
+ var d = $q.defer();
- swellRT.createModel(function(model){
- openedProjects[model.id()] = d.promise;
+ swellRT.createModel(function (model) {
+ openedProjects[model.id()] = d.promise;
- SwellRTCommon.makeModelPublic(model);
+ SwellRTCommon.makeModelPublic(model);
- var proxyProj;
+ var proxyProj;
- $timeout(function(){
- proxyProj = swellRT.proxy(model, Project);
+ $timeout(function () {
+ proxyProj = swellRT.proxy(model, Project);
+ });
+
+ $timeout(function () {
+ proxyProj.type = 'project';
+ proxyProj.communities =
+ (options.communityId) ? [options.communityId] : [];
+ proxyProj.id = model.id();
+ proxyProj.title = '';
+ proxyProj.chat = [];
+ proxyProj.pad = new swellRT.TextObject();
+ proxyProj.attachments = {};
+ proxyProj.needs = [];
+ proxyProj.lastAccesses = [];
+ proxyProj.promoter = User.currentId();
+ proxyProj.supporters = [];
+ proxyProj.shareMode = 'public';
+ d.resolve(proxyProj);
+ });
});
- $timeout(function(){
- proxyProj.type = 'project';
- proxyProj.communities =
- (options.communityId) ? [ options.communityId ] : [];
- proxyProj.id = model.id();
- proxyProj.title = '';
- proxyProj.chat = [];
- proxyProj.pad = new swellRT.TextObject();
- proxyProj.attachments = {};
- proxyProj.needs = [];
- proxyProj.lastAccesses = [];
- proxyProj.promoter = User.currentId();
- proxyProj.supporters = [];
- proxyProj.shareMode = 'public';
- d.resolve(proxyProj);
+ d.promise.then(callback);
+
+ return d.promise;
+ }
+
+ function updateTrello() {
+ $location.path(localStorage.getItem('projectUrl'));
+ let locationSplit = $location.path().split('/');
+ let urlId = locationSplit[locationSplit.length - 1];
+ let id = base64.urldecode(urlId);
+ if (!localStorage.getItem('trelloTeemToken')) return;
+ find(id).then((model) => {
+ model.trello = {};
+ model.trello.token = localStorage.getItem('trelloTeemToken');
+ localStorage.removeItem('trelloTeemToken');
+ trelloSvc.createTrelloBoard(model).then((BoardData) => {
+ model.trello.boardId = BoardData.id;
+ trelloSvc.createNewList(model.trello, 'Teem').then((listData) => {
+ model.trello.listId = listData.id;
+ trelloSvc.createNewList(model.trello, 'Done').
+ then((doneListData) => {
+ model.trello.doneListId = doneListData.id;
+ })
+ .catch(err => console.log(err));
+ })
+ .catch(err => console.log(err));
+ })
+ .catch(err => console.error(err));
});
- });
-
- d.promise.then(callback);
-
- return d.promise;
- }
-
- var projectListProjection = {
- participants: 1,
- root: {
- title: 1,
- image: 1,
- id: 1,
- _urlId: 1,
- type: 1,
- featured: 1,
- communities: 1,
- pad: {
- lastmodtime: 1
- }
}
- };
-
- return {
- all,
- contributing,
- findByUrlId,
- find,
- create,
- projectListProjection
- };
- }]);
+
+ var projectListProjection = {
+ participants: 1,
+ root: {
+ title: 1,
+ image: 1,
+ id: 1,
+ _urlId: 1,
+ type: 1,
+ featured: 1,
+ communities: 1,
+ pad: {
+ lastmodtime: 1
+ }
+ }
+ };
+
+ return {
+ all,
+ contributing,
+ findByUrlId,
+ find,
+ create,
+ projectListProjection,
+ updateTrello
+ };
+ }]);
diff --git a/src/js/services/trelloService.js b/src/js/services/trelloService.js
new file mode 100644
index 00000000..8c3d89b5
--- /dev/null
+++ b/src/js/services/trelloService.js
@@ -0,0 +1,104 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('Teem')
+ .factory('trelloSvc', trelloSvc);
+ trelloSvc.$inject = ['$http', '$location', '$window', '$rootScope', '$timeout', '$q'];
+ function trelloSvc($http, $location, $window, $rootScope, $timeout, $q) {
+ const AUTH_ROUTE = 'https://trello.com/1/authorize';
+
+ function getToken() {
+ localStorage.setItem('projectUrl', $location.path());
+ const redirUrl = 'https://trello.com/1/authorize?expiration=never&name="Teem"&key=09e4aced60041e389dbb27b9accadd65&callback_method=fragment&scope=read%2Cwrite%2Caccount&return_url=http://localhost:8000/trello/get/';
+ $window.location.href = redirUrl;
+ }
+
+ function parseTokenFromUrl(urlString) {
+ console.log(urlString);
+ }
+
+ function createTrelloBoard(prObj, need) {
+ let deferred = $q.defer();
+ if (!prObj.trello.token) {
+ getToken();
+ deferred.resolve();
+ return deferred.promise;
+ }
+ const BoardApiURL = `https://api.trello.com/1/boards/?name=${prObj.id}&key=09e4aced60041e389dbb27b9accadd65&token=${prObj.trello.token}`;
+ $http.post(BoardApiURL).then((result) => {
+ console.log(result);
+ deferred.resolve(result.data);
+ })
+ .catch((err) => {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ }
+
+ function createNewList(trelloObj) {
+ let deferred = $q.defer();
+ if (!trelloObj.boardId) {
+ deferred.reject('No trello Board Id found!');
+ }
+ else {
+ const listAPIendPoint = `https://api.trello.com/1/lists?name=Teem&idBoard=${trelloObj.boardId}&key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}`;
+ $http.post(listAPIendPoint)
+ .then((data) => {
+ console.log(data);
+ })
+ .catch(err => console.log(err));
+ }
+ return deferred.promise;
+ }
+
+ function addNewCard(trelloObj, need) {
+ let deferred = $q.defer();
+ const newCardURL = `https://api.trello.com/1/cards?idList=${trelloObj.listId}&name=${need.text}&key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}`;
+ $http.post(newCardURL).
+ then(result => deferred.resolve(result.data))
+ .catch(err => deferred.resolve(err));
+ return deferred.promise;
+ }
+
+ function archiveCard(trelloObj, need){
+ let deferred = $q.defer();
+ const removeCardURL = `https://api.trello.com/1/cards/${need.trelloId}/idList?&key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}&value=${trelloObj.doneListId}`;
+ $http.delete(removeCardURL).
+ then(result => deferred.resolve(result.data))
+ .catch(err => deferred.reject(err));
+ return deferred.promise;
+ }
+
+ function unarchiveCard(trelloObj, need){
+ let deferred = $q.defer();
+ const archiveCardURL = `https://api.trello.com/1/cards/${need.trelloId}?idList?&key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}&value=${trelloObj.listId}`;
+ $http.put(archiveCardURL).
+ then(result => deferred.resolve(result.data))
+ .catch(err => deferred.reject(err));
+ return deferred.promise;
+ }
+
+ function addNewComment(trelloObj, need, comment){
+ let deferred = $q.defer();
+ const newCommentURL = `https://api.trello.com/1/cards/${need.trelloId}/actions/comments?key=09e4aced60041e389dbb27b9accadd65&token=${trelloObj.token}&text=${comment}`;
+ $http.post(newCommentURL).
+ then(result => deferred.resolve(result.data))
+ .catch(err => deferred.reject(err));
+ return deferred.promise;
+ }
+
+ return {
+ getToken: getToken,
+ parseTokenFromUrl: parseTokenFromUrl,
+ createTrelloBoard: createTrelloBoard,
+ createNewList: createNewList,
+ addNewCard: addNewCard,
+ archiveCard: archiveCard,
+ addNewComment: addNewComment,
+ unarchiveCard: unarchiveCard
+ };
+
+ return service;
+ }
+})();
diff --git a/src/templates/pad.html b/src/templates/pad.html
index db88fc30..83009723 100644
--- a/src/templates/pad.html
+++ b/src/templates/pad.html
@@ -66,3 +66,5 @@
+
+
diff --git a/src/templates/projects/project/desktop.html b/src/templates/projects/project/desktop.html
index 7f4d870f..3dcc84af 100644
--- a/src/templates/projects/project/desktop.html
+++ b/src/templates/projects/project/desktop.html
@@ -54,8 +54,9 @@
+
+
need.nav.title