This commit is contained in:
Julius Haertl
2016-06-15 14:05:19 +02:00
parent ae9d5da329
commit cf3bbcb888
35 changed files with 686 additions and 465 deletions

View File

@@ -99,6 +99,7 @@
<type>text</type>
<notnull>true</notnull>
<length>64</length>
<default>plain</default>
</field>
<field>
<name>last_modified</name>

View File

@@ -34,9 +34,10 @@ return [
['name' => 'stack#update', 'url' => '/stacks/', 'verb' => 'PUT'],
['name' => 'stack#delete', 'url' => '/stacks/{stackId}/', 'verb' => 'DELETE'],
// cards
['name' => 'card#index', 'url' => '/cards/{stackId}/', 'verb' => 'GET'],
['name' => 'card#read', 'url' => '/cards/{cardId}/', 'verb' => 'GET'],
['name' => 'card#create', 'url' => '/cards/', 'verb' => 'POST'],
['name' => 'card#update', 'url' => '/cards/', 'verb' => 'PUT'],
['name' => 'card#reorder', 'url' => '/cards/reorder/', 'verb' => 'PUT'],
['name' => 'card#delete', 'url' => '/cards/{cardId}/', 'verb' => 'DELETE'],
// TODO: Implement public board sharing

View File

@@ -1,5 +1,6 @@
<?php
// TODO: Implement LATER
namespace OCA\Deck\Controller;
use OCA\Deck\Service\BoardService;
@@ -13,11 +14,11 @@ class BoardApiController extends BaseApiController {
private $userId;
public function __construct($appName,
IRequest $request,
BoardService $stackService,
BoardService $cardService,
$userId){
parent::__construct($appName, $request);
$this->userId = $userId;
$this->boardService = $stackService;
$this->boardService = $cardService;
}
/**
* @NoAdminRequired

View File

@@ -14,11 +14,11 @@ class BoardController extends Controller {
private $boardService;
public function __construct($appName,
IRequest $request,
BoardService $stackService,
BoardService $cardService,
$userId){
parent::__construct($appName, $request);
$this->userId = $userId;
$this->boardService = $stackService;
$this->boardService = $cardService;
}
/**
* @NoAdminRequired
@@ -32,7 +32,7 @@ class BoardController extends Controller {
*/
public function read($boardId) {
// FIXME: Remove as this is just for testing if loading animation works out nicely
usleep(500000);
usleep(200000);
return $this->boardService->find($this->userId, $boardId);
}
/**

View File

@@ -0,0 +1,57 @@
<?php
namespace OCA\Deck\Controller;
use OCA\Deck\Service\CardService;
use OCP\IRequest;
use OCP\AppFramework\Controller;
class CardController extends Controller {
private $userId;
private $cardService;
public function __construct($appName,
IRequest $request,
CardService $cardService,
$userId){
parent::__construct($appName, $request);
$this->userId = $userId;
$this->cardService = $cardService;
}
/**
* @NoAdminRequired
*/
public function index($cardId) {
return $this->cardService->findAll($boardId);
}
/**
* @NoAdminRequired
*/
public function read($cardId) {
return $this->cardService->find($this->userId, $cardId);
}
/**
* @NoAdminRequired
*/
public function reorder($cardId, $stackId, $order) {
return $this->cardService->reorder($cardId, $stackId, $order);
}
/**
* @NoAdminRequired
*/
public function create($title, $stackId, $type, $order=999) {
return $this->cardService->create($title, $stackId, $type, $order, $this->userId);
}
/**
* @NoAdminRequired
*/
public function update($id, $title, $stackId, $type, $order) {
return $this->cardService->update($id, $title, $stackId, $type, $order, $this->userId);
}
/**
* @NoAdminRequired
*/
public function delete($cardId) {
return $this->cardService->delete($this->userId, $cardId);
}
}

View File

@@ -27,27 +27,15 @@ class PageController extends Controller {
}
/**
* CAUTION: the @Stuff turns off security checks; for this page no admin is
* required and no CSRF check. If you don't know what CSRF is, read
* it up in the docs or you might create a security hole. This is
* basically the only required method to add this exemption, don't
* add it to any other method if you don't exactly know what it does
* Handle main html view from templates/main.php
* This will return the main angular application
*
* @NoAdminRequired
* @NoCSRFRequired
*/
public function index() {
$params = ['user' => $this->userId];
return new TemplateResponse('deck', 'main', $params); // templates/main.php
return new TemplateResponse('deck', 'main', $params);
}
/**
* Simply method that posts back the payload of the request
* @NoAdminRequired
*/
public function doEcho($echo) {
return new DataResponse(['echo' => $echo]);
}
}

View File

@@ -14,11 +14,11 @@ class StackController extends Controller {
private $stackService;
public function __construct($appName,
IRequest $request,
StackService $stackService,
StackService $cardService,
$userId){
parent::__construct($appName, $request);
$this->userId = $userId;
$this->stackService = $stackService;
$this->stackService = $cardService;
}
/**
* @NoAdminRequired

View File

@@ -1,16 +1,3 @@
/*li:hover .app-navigation-entry-utils-buttom {
display: inline-block;
}
li .app-navigation-entry-utils-buttom,
li:hover .app-navigation-entry-utils-counter {
display: none;
}
.app-navigation-entry-utils button {
border:none;
color: #aaa !important;
background-color: #ffffff !important;
}
*/
.app-navigation-entry-utils-menu-button {
display: block !important;
}
@@ -36,10 +23,12 @@ li:hover .app-navigation-entry-utils-counter {
background-color:#fff;
}
#board {
position-relative;
position: relative;
height:100%;
white-space: nowrap; /* important */
overflow: auto;
background-color:#ffffff;
padding:0;
}
#board #innerBoard {
@@ -53,10 +42,10 @@ li:hover .app-navigation-entry-utils-counter {
padding:10px;
position: fixed;
width:100%;
color: #fff;
color: #333333;
padding-right:250px;
z-index:100;
background-color:#aaa;
background-color:#ffffff;
}
#board .board-actions {
position:absolute;
@@ -71,10 +60,7 @@ li:hover .app-navigation-entry-utils-counter {
}
.stack {
width:320px;
background-color:#f1f1f1;
border-radius: 3px;
margin-right:10px;
-webkit-box-shadow: 0px 0px 3px 0px #aaa;
vertical-align: top;
display:inline-block !important;
}
@@ -113,7 +99,8 @@ li:hover .app-navigation-entry-utils-counter {
float:right;
}
.card {
background-color:#fff;
background-color:#fafafa;
border: 1px solid #aaa;
margin:5px;
padding:5px;
padding-bottom:4px;
@@ -140,6 +127,12 @@ li:hover .app-navigation-entry-utils-counter {
vertical-align: middle;
}
.as-sortable-placeholder {
margin:5px;
margin-bottom:5px;
border: 1px dashed #aaa;
}
.labels {
position:absolute;
top:0px;
@@ -172,12 +165,17 @@ li:hover .app-navigation-entry-utils-counter {
.card.create {
text-align:center;
opacity: 0.6;
margin:0;
border: none;
}
.card.create:hover {
text-align:center;
opacity: 1;
}
.card.create h3 {
margin:0;
padding:0;
}
.card.create h3 input {
width:100%;
border:0px;
@@ -187,9 +185,18 @@ li:hover .app-navigation-entry-utils-counter {
font-size:10pt;
margin:0;
padding:0;
border-bottom:1px solid #aaa;
border-bottom:1px solid #ffffff;
border-radius: 0px;
margin-bottom:20px;
color: #ffffff;
background-color: transparent;
}
.card.create .fa {
color:#ffffff;
width:100%;
}
.card.create .fa:hover {
opacity: 0.5;
cursor: pointer;
}
.due {
background-color:#eee;

View File

@@ -2,6 +2,7 @@
namespace OCA\Deck\Db;
use OCP\AppFramework\Db\Entity;
use OCP\IDb;
use OCP\AppFramework\Db\Mapper;
@@ -29,5 +30,8 @@ class BoardMapper extends Mapper {
return $this->findEntities($sql, [$userId], $limit, $offset);
}
public function delete(Entity $entity) {
// FIXME: delete linked elements, because owncloud doesn't support foreign keys for apps
return parent::delete($entity);
}
}

36
db/card.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
// db/author.php
namespace OCA\Deck\Db;
use JsonSerializable;
use OCP\AppFramework\Db\Entity;
class Card extends Entity implements JsonSerializable {
public $id;
protected $title;
protected $stackId;
protected $type;
protected $lastModified;
protected $createdAt;
protected $owner;
protected $order;
public function __construct() {
$this->addType('id','integer');
$this->addType('stackId','integer');
$this->addType('order','integer');
}
public function jsonSerialize() {
return [
'id' => $this->id,
'title' => $this->title,
'type' => $this->type,
'lastModified' => $this->lastModified,
'createdAt' => $this->createdAt,
'owner' => $this->owner,
'order' => $this->order,
'stackId' => $this->stackId,
];
}
}

39
db/cardmapper.php Normal file
View File

@@ -0,0 +1,39 @@
<?php
namespace OCA\Deck\Db;
use OCP\AppFramework\Db\Entity;
use OCP\IDb;
use OCP\AppFramework\Db\Mapper;
class CardMapper extends Mapper {
public function __construct(IDb $db) {
parent::__construct($db, 'deck_cards', '\OCA\Deck\Db\Card');
}
/**
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result
*/
public function find($id) {
$sql = 'SELECT * FROM `*PREFIX*deck_cards` ' .
'WHERE `id` = ?';
return $this->findEntity($sql, [$id]);
}
public function findAll($stackId, $limit=null, $offset=null) {
$sql = 'SELECT * FROM `*PREFIX*deck_cards` WHERE `stack_id` = ? ORDER BY `order`';
return $this->findEntities($sql, [$stackId], $limit, $offset);
}
public function delete(Entity $entity) {
// FIXME: delete linked elements, because owncloud doesn't support foreign keys for apps
return parent::delete($entity);
}
}

View File

@@ -10,6 +10,7 @@ class Stack extends Entity implements JsonSerializable {
public $id;
protected $title;
protected $boardId;
protected $cards = array();
protected $order;
public function __construct() {
$this->addType('id','integer');
@@ -17,12 +18,16 @@ class Stack extends Entity implements JsonSerializable {
$this->addType('order','integer');
}
public function setCards($cards) {
$this->cards = $cards;
}
public function jsonSerialize() {
return [
'id' => $this->id,
'title' => $this->title,
'order' => $this->order,
'boardId' => $this->boardId
'boardId' => $this->boardId,
'cards' => $this->cards,
];
}
}

View File

@@ -2,14 +2,18 @@
namespace OCA\Deck\Db;
use OCP\AppFramework\Db\Entity;
use OCP\IDb;
use OCP\AppFramework\Db\Mapper;
class StackMapper extends Mapper {
public function __construct(IDb $db) {
private $cardMapper;
public function __construct(IDb $db, CardMapper $cardMapper) {
parent::__construct($db, 'deck_stacks', '\OCA\Deck\Db\Stack');
$this->cardMapper = $cardMapper;
}
@@ -29,5 +33,8 @@ class StackMapper extends Mapper {
return $this->findEntities($sql, [$boardId], $limit, $offset);
}
public function delete(Entity $entity) {
// FIXME: delete linked elements, because owncloud doesn't support foreign keys for apps
return parent::delete($entity);
}
}

View File

@@ -1,78 +1,2 @@
var app = angular.module('Deck', ['ngRoute', 'ngSanitize', 'ui.router', 'as.sortable']);
app.config(function ($provide, $routeProvider, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider) {
'use strict';
$httpProvider.defaults.headers.common.requesttoken = oc_requesttoken;
$urlRouterProvider.otherwise("/");
$stateProvider
.state('list', {
url: "/",
templateUrl: "/boardlist.mainView.html",
controller: 'ListController',
})
.state('board', {
url: "/board/:boardId",
templateUrl: "/board.html",
controller: 'BoardController'
})
.state('board.card', {
url: "/card/:cardId",
views: {
"sidebarView": {
templateUrl: "/card.sidebarView.html",
controller: 'CardController'
}
}
})
.state('board.settings', {})
.state('board.sharing', {});
});
// OwnCloud Click Handling
// https://doc.owncloud.org/server/8.0/developer_manual/app/css.html
app.directive('appNavigationEntryUtils', function () {
'use strict';
return {
restrict: 'C',
link: function (scope, elm) {
var menu = elm.siblings('.app-navigation-entry-menu');
var button = $(elm)
.find('.app-navigation-entry-utils-menu-button button');
button.click(function () {
menu.toggleClass('open');
});
scope.$on('documentClicked', function (scope, event) {
if (event.target !== button[0]) {
menu.removeClass('open');
}
});
}
};
});
app.directive('autofocusOnInsert', function () {
'use strict';
return function (scope, elm) {
elm.focus();
};
});
app.run(function ($document, $rootScope, $transitions) {
'use strict';
$document.click(function (event) {
$rootScope.$broadcast('documentClicked', event);
});
$transitions.onEnter({to: 'board.card'}, function ($state, $transition$) {
$rootScope.sidebar.show = true;
});
$transitions.onEnter({to: 'board'}, function ($state) {
$rootScope.sidebar.show = false;
});
$transitions.onExit({from: 'board.card'}, function ($state) {
$rootScope.sidebar.show = false;
});
});

35
js/app/Config.js Normal file
View File

@@ -0,0 +1,35 @@
app.config(function ($provide, $routeProvider, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider, $compileProvider) {
'use strict';
$httpProvider.defaults.headers.common.requesttoken = oc_requesttoken;
$compileProvider.debugInfoEnabled(true);
$urlRouterProvider.otherwise("/");
$stateProvider
.state('list', {
url: "/",
templateUrl: "/boardlist.mainView.html",
controller: 'ListController',
})
.state('board', {
url: "/board/:boardId",
templateUrl: "/board.html",
controller: 'BoardController'
})
.state('board.card', {
url: "/card/:cardId",
views: {
"sidebarView": {
templateUrl: "/card.sidebarView.html",
controller: 'CardController'
}
}
})
.state('board.settings', {
})
.state('board.sharing', {
});
});

15
js/app/Run.js Normal file
View File

@@ -0,0 +1,15 @@
app.run(function ($document, $rootScope, $transitions) {
'use strict';
$document.click(function (event) {
$rootScope.$broadcast('documentClicked', event);
});
$transitions.onEnter({to: 'board.card'}, function ($state, $transition$) {
$rootScope.sidebar.show = true;
});
$transitions.onEnter({to: 'board'}, function ($state) {
$rootScope.sidebar.show = false;
});
$transitions.onExit({from: 'board.card'}, function ($state) {
$rootScope.sidebar.show = false;
});
});

View File

@@ -1,72 +1,33 @@
app.controller('BoardController', function ($rootScope, $scope, $location, $http, $route, $stateParams, BoardService, StackService) {
app.controller('BoardController', function ($rootScope, $scope, $stateParams, StatusService, BoardService, StackService, CardService) {
$scope.sidebar = $rootScope.sidebar;
$scope.id = $stateParams.boardId;
$scope.stackservice = StackService;
$scope.boardservice = BoardService;
$scope.statusservice = StatusService;
// fetch data
StackService.clear();
console.log("foo");
StackService.fetchAll($scope.id).then(function(data) {
console.log($scope.stackservice.data)
$scope.releaseWaiting();
$scope.statusservice.releaseWaiting();
}, function(error) {
$scope.setError('Error occured', error);
$scope.statusservice.setError('Error occured', error);
});
BoardService.fetchOne($scope.id).then(function(data) {
$scope.releaseWaiting();
$scope.statusservice.releaseWaiting();
}, function(error) {
$scope.setError('Error occured', error);
$scope.statusservice.setError('Error occured', error);
});
$scope.newStack = { 'boardId': $scope.id};
$scope.newCard = {};
// Status Helper
$scope.status = {
'active': true,
'icon': 'loading',
'title': 'Bitte warten',
'text': 'Es dauert noch einen kleinen Moment',
'counter': 2,
};
$scope.setStatus = function($icon, $title, $text='') {
$scope.status.active = true;
$scope.status.icon = $icon;
$scope.status.title = $title;
$scope.status.text = $text;
}
$scope.setError = function($title, $text) {
$scope.status.active = true;
$scope.status.icon = 'error';
$scope.status.title = $title;
$scope.status.text = $text;
$scope.status.counter = 0;
}
$scope.releaseWaiting = function() {
if($scope.status.counter>0)
$scope.status.counter--;
if($scope.status.counter==0) {
$scope.status = {
'active': false
}
}
}
$scope.unsetStatus = function() {
$scope.status = {
'active': false
}
}
// Create a new Stack
$scope.createStack = function () {
@@ -75,6 +36,18 @@ app.controller('BoardController', function ($rootScope, $scope, $location, $http
});
};
$scope.createCard = function(stack, title) {
var newCard = {
'title': title,
'stackId': stack,
'type': 'plain',
};
CardService.create(newCard).then(function (data) {
$scope.stackservice.addCard(data);
$scope.newCard.title = "";
});
}
// Lighten Color of the board for background usage
$scope.rgblight = function (hex) {
var result = /^([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})$/i.exec(hex);
@@ -95,10 +68,23 @@ app.controller('BoardController', function ($rootScope, $scope, $location, $http
// settings for card sorting
$scope.sortOptions = {
itemMoved: function (event) {
// TODO: Implement reodering here
event.source.itemScope.modelValue.status = event.dest.sortableScope.$parent.column;
console.log(event.dest.sortableScope.$parent);
var order = event.dest.index;
var card = event.source.itemScope.c;
var newStack = event.dest.sortableScope.$parent.s.id;
card.stackId = newStack;
CardService.update(card);
CardService.reorder(card, order).then(function(data) {
StackService.data[newStack].cards = data;
});
},
orderChanged: function (event) {
// TODO: Implement ordering here
var order = event.dest.index;
var card = event.source.itemScope.c;
CardService.reorder(card, order);
},
scrollableContainer: '#board',
containerPositioning: 'relative',

View File

@@ -1,16 +1,16 @@
app.controller('CardController', function ($scope, $rootScope, $routeParams, $location, $stateParams) {
app.controller('CardController', function ($scope, $rootScope, $routeParams, $location, $stateParams, CardService) {
$scope.sidebar = $rootScope.sidebar;
$scope.location = $location;
$scope.card = {'id': 1, 'title': 'We should implement all the useful things, that a kanban like project managemnt system needs for having success', 'description': 'Non et quibusdam officiis expedita excepturi. Tenetur ea et dignissimos qui. Rerum quis commodi aperiam amet dolorum suscipit asperiores. Enim dolorem ea nisi voluptate. \
Consequatur enim iste dolore autem est unde voluptatum. Aut sit et iure. Suscipit deserunt nisi repellat in officiis alias. Nihil beatae ea ut laudantium at.\
Doloribus nihil ipsa consequatur laudantium qui enim eveniet quo. Voluptatum tenetur sunt quis sint aliquam et molestias. Quae voluptatem tempora qui eaque qui esse possimus magni. Animi dolorem maiores iste.\
Totam ut tempora officiis ipsam dolorem modi. Dolores hic aut itaque. Earum in est voluptas voluptatum. Cumque pariatur qui omnis placeat. Eius sed sunt corrupti dolorem quo.'};
$scope.cardservice = CardService;
$scope.cardId = $stateParams.cardId;
console.log($stateParams);
CardService.fetchOne($scope.cardId).then(function(data) {
console.log(data);
}, function(error) {
});

View File

@@ -1,70 +1,39 @@
app.controller('ListController', function ($scope, $location, boardFactory, BoardService) {
app.controller('ListController', function ($scope, $location, BoardService) {
$scope.boards = null;
$scope.newBoard = {};
$scope.status = {};
$scope.colors = ['31CC7C', '317CCC', 'FF7A66', 'F1DB50', '7C31CC', 'CC317C', '3A3B3D', 'CACBCD'];
$scope.boardservice = BoardService;
BoardService.fetchAll().then(function(data) {
console.log($scope.boardservice);
console.log(data);
}, function(error) {
//$scope.setStatus('error','Error occured', error);
});
$scope.getBoards = function() {
boardFactory.getBoards()
.then(function (response) {
$scope.boards = response.data;
for (var i = 0; i < $scope.boards.length; i++) {
$scope.boards[i].status = {
'edit': false,
}
}
}, function (error) {
$scope.status.getBoards = 'Unable to load customer data: ' + error.message;
});
}
BoardService.fetchAll(); // TODO: show error when loading fails
$scope.selectColor = function(color) {
$scope.newBoard.color = color;
};
$scope.createBoard = function () {
boardFactory.createBoard($scope.newBoard)
BoardService.create($scope.newBoard)
.then(function (response) {
$scope.boards.push(response.data);
$scope.newBoard = {};
$scope.status.addBoard=false;
}, function(error) {
$scope.status.createBoard = 'Unable to insert board: ' + error.message;
});
};
$scope.updateBoard = function(board) {
boardFactory.updateBoard(board)
.then(function (response) {
board = response.data;
}, function(error) {
$scope.status.createBoard = 'Unable to insert board: ' + error.message;
});
BoardService.update(board);
board.status.edit = false;
$scope.$apply();
};
$scope.deleteBoard = function(board) {
// TODO: Ask for confirmation
//if (confirm('Are you sure you want to delete this?')) {
BoardService.delete(board.id);
//}
};
$scope.selectColor = function(color) {
$scope.newBoard.color = color;
};
$scope.deleteBoard = function (index) {
var board = $scope.boards[index];
boardFactory.deleteBoard(board.id)
.then(function (response) {
$scope.status.deleteBoard = 'Deleted Board';
$scope.boards.splice( index, 1 );
}, function(error) {
$scope.status.deleteBoard = 'Unable to insert board: ' + error.message;
});
};
$scope.getBoards();
});

View File

@@ -0,0 +1,24 @@
// OwnCloud Click Handling
// https://doc.owncloud.org/server/8.0/developer_manual/app/css.html
app.directive('appNavigationEntryUtils', function () {
'use strict';
return {
restrict: 'C',
link: function (scope, elm) {
var menu = elm.siblings('.app-navigation-entry-menu');
var button = $(elm)
.find('.app-navigation-entry-utils-menu-button button');
button.click(function () {
menu.toggleClass('open');
});
scope.$on('documentClicked', function (scope, event) {
if (event.target !== button[0]) {
menu.removeClass('open');
}
});
}
};
});

View File

@@ -0,0 +1,6 @@
app.directive('autofocusOnInsert', function () {
'use strict';
return function (scope, elm) {
elm.focus();
};
});

View File

@@ -1,10 +1,13 @@
var app = angular.module('Deck', ['ngRoute', 'ngSanitize', 'ui.router', 'as.sortable']);
app.config(function ($provide, $routeProvider, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider) {
app.config(["$provide", "$routeProvider", "$interpolateProvider", "$httpProvider", "$urlRouterProvider", "$stateProvider", "$compileProvider", function ($provide, $routeProvider, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider, $compileProvider) {
'use strict';
$httpProvider.defaults.headers.common.requesttoken = oc_requesttoken;
$compileProvider.debugInfoEnabled(true);
$urlRouterProvider.otherwise("/");
$stateProvider
@@ -27,42 +30,14 @@ app.config(function ($provide, $routeProvider, $interpolateProvider, $httpProvid
}
}
})
.state('board.settings', {})
.state('board.sharing', {});
});
.state('board.settings', {
// OwnCloud Click Handling
// https://doc.owncloud.org/server/8.0/developer_manual/app/css.html
app.directive('appNavigationEntryUtils', function () {
'use strict';
return {
restrict: 'C',
link: function (scope, elm) {
})
.state('board.sharing', {
var menu = elm.siblings('.app-navigation-entry-menu');
var button = $(elm)
.find('.app-navigation-entry-utils-menu-button button');
button.click(function () {
menu.toggleClass('open');
});
scope.$on('documentClicked', function (scope, event) {
if (event.target !== button[0]) {
menu.removeClass('open');
}
});
}
};
});
app.directive('autofocusOnInsert', function () {
'use strict';
return function (scope, elm) {
elm.focus();
};
});
app.run(function ($document, $rootScope, $transitions) {
}]);
app.run(["$document", "$rootScope", "$transitions", function ($document, $rootScope, $transitions) {
'use strict';
$document.click(function (event) {
$rootScope.$broadcast('documentClicked', event);
@@ -76,83 +51,45 @@ app.run(function ($document, $rootScope, $transitions) {
$transitions.onExit({from: 'board.card'}, function ($state) {
$rootScope.sidebar.show = false;
});
});
}]);
app.controller('AppController', function ($scope, $location, $http, $route, $log, $rootScope, $stateParams) {
app.controller('AppController', ["$scope", "$location", "$http", "$route", "$log", "$rootScope", "$stateParams", function ($scope, $location, $http, $route, $log, $rootScope, $stateParams) {
$rootScope.sidebar = {
show: false
};
$scope.sidebar = $rootScope.sidebar;
});
app.controller('BoardController', function ($rootScope, $scope, $location, $http, $route, $stateParams, BoardService, StackService) {
}]);
app.controller('BoardController', ["$rootScope", "$scope", "$stateParams", "StatusService", "BoardService", "StackService", "CardService", function ($rootScope, $scope, $stateParams, StatusService, BoardService, StackService, CardService) {
$scope.sidebar = $rootScope.sidebar;
$scope.id = $stateParams.boardId;
$scope.stackservice = StackService;
$scope.boardservice = BoardService;
$scope.statusservice = StatusService;
// fetch data
StackService.clear();
console.log("foo");
StackService.fetchAll($scope.id).then(function(data) {
console.log($scope.stackservice.data)
$scope.releaseWaiting();
$scope.statusservice.releaseWaiting();
}, function(error) {
$scope.setError('Error occured', error);
$scope.statusservice.setError('Error occured', error);
});
BoardService.fetchOne($scope.id).then(function(data) {
$scope.releaseWaiting();
$scope.statusservice.releaseWaiting();
}, function(error) {
$scope.setError('Error occured', error);
$scope.statusservice.setError('Error occured', error);
});
$scope.newStack = { 'boardId': $scope.id};
$scope.newCard = {};
// Status Helper
$scope.status = {
'active': true,
'icon': 'loading',
'title': 'Bitte warten',
'text': 'Es dauert noch einen kleinen Moment',
'counter': 2,
};
$scope.setStatus = function($icon, $title, $text='') {
$scope.status.active = true;
$scope.status.icon = $icon;
$scope.status.title = $title;
$scope.status.text = $text;
}
$scope.setError = function($title, $text) {
$scope.status.active = true;
$scope.status.icon = 'error';
$scope.status.title = $title;
$scope.status.text = $text;
$scope.status.counter = 0;
}
$scope.releaseWaiting = function() {
if($scope.status.counter>0)
$scope.status.counter--;
if($scope.status.counter==0) {
$scope.status = {
'active': false
}
}
}
$scope.unsetStatus = function() {
$scope.status = {
'active': false
}
}
// Create a new Stack
$scope.createStack = function () {
@@ -161,6 +98,18 @@ app.controller('BoardController', function ($rootScope, $scope, $location, $http
});
};
$scope.createCard = function(stack, title) {
var newCard = {
'title': title,
'stackId': stack,
'type': 'plain',
};
CardService.create(newCard).then(function (data) {
$scope.stackservice.addCard(data);
$scope.newCard.title = "";
});
}
// Lighten Color of the board for background usage
$scope.rgblight = function (hex) {
var result = /^([A-Fa-f\d]{2})([A-Fa-f\d]{2})([A-Fa-f\d]{2})$/i.exec(hex);
@@ -181,10 +130,23 @@ app.controller('BoardController', function ($rootScope, $scope, $location, $http
// settings for card sorting
$scope.sortOptions = {
itemMoved: function (event) {
// TODO: Implement reodering here
event.source.itemScope.modelValue.status = event.dest.sortableScope.$parent.column;
console.log(event.dest.sortableScope.$parent);
var order = event.dest.index;
var card = event.source.itemScope.c;
var newStack = event.dest.sortableScope.$parent.s.id;
card.stackId = newStack;
CardService.update(card);
CardService.reorder(card, order).then(function(data) {
StackService.data[newStack].cards = data;
});
},
orderChanged: function (event) {
// TODO: Implement ordering here
var order = event.dest.index;
var card = event.source.itemScope.c;
CardService.reorder(card, order);
},
scrollableContainer: '#board',
containerPositioning: 'relative',
@@ -210,21 +172,21 @@ app.controller('BoardController', function ($rootScope, $scope, $location, $http
}
};
});
}]);
app.controller('CardController', function ($scope, $rootScope, $routeParams, $location, $stateParams) {
app.controller('CardController', ["$scope", "$rootScope", "$routeParams", "$location", "$stateParams", "CardService", function ($scope, $rootScope, $routeParams, $location, $stateParams, CardService) {
$scope.sidebar = $rootScope.sidebar;
$scope.location = $location;
$scope.card = {'id': 1, 'title': 'We should implement all the useful things, that a kanban like project managemnt system needs for having success', 'description': 'Non et quibusdam officiis expedita excepturi. Tenetur ea et dignissimos qui. Rerum quis commodi aperiam amet dolorum suscipit asperiores. Enim dolorem ea nisi voluptate. \
Consequatur enim iste dolore autem est unde voluptatum. Aut sit et iure. Suscipit deserunt nisi repellat in officiis alias. Nihil beatae ea ut laudantium at.\
Doloribus nihil ipsa consequatur laudantium qui enim eveniet quo. Voluptatum tenetur sunt quis sint aliquam et molestias. Quae voluptatem tempora qui eaque qui esse possimus magni. Animi dolorem maiores iste.\
Totam ut tempora officiis ipsam dolorem modi. Dolores hic aut itaque. Earum in est voluptas voluptatum. Cumque pariatur qui omnis placeat. Eius sed sunt corrupti dolorem quo.'};
$scope.cardservice = CardService;
$scope.cardId = $stateParams.cardId;
console.log($stateParams);
CardService.fetchOne($scope.cardId).then(function(data) {
console.log(data);
}, function(error) {
});
@@ -234,81 +196,81 @@ app.controller('CardController', function ($scope, $rootScope, $routeParams, $lo
$scope.$apply();
});*/
});
}]);
app.controller('ListController', function ($scope, $location, boardFactory, BoardService) {
app.controller('ListController', ["$scope", "$location", "BoardService", function ($scope, $location, BoardService) {
$scope.boards = null;
$scope.newBoard = {};
$scope.status = {};
$scope.colors = ['31CC7C', '317CCC', 'FF7A66', 'F1DB50', '7C31CC', 'CC317C', '3A3B3D', 'CACBCD'];
$scope.boardservice = BoardService;
BoardService.fetchAll().then(function(data) {
console.log($scope.boardservice);
console.log(data);
}, function(error) {
//$scope.setStatus('error','Error occured', error);
});
$scope.getBoards = function() {
boardFactory.getBoards()
.then(function (response) {
$scope.boards = response.data;
for (var i = 0; i < $scope.boards.length; i++) {
$scope.boards[i].status = {
'edit': false,
}
}
}, function (error) {
$scope.status.getBoards = 'Unable to load customer data: ' + error.message;
});
}
BoardService.fetchAll(); // TODO: show error when loading fails
$scope.selectColor = function(color) {
$scope.newBoard.color = color;
};
$scope.createBoard = function () {
boardFactory.createBoard($scope.newBoard)
BoardService.create($scope.newBoard)
.then(function (response) {
$scope.boards.push(response.data);
$scope.newBoard = {};
$scope.status.addBoard=false;
}, function(error) {
$scope.status.createBoard = 'Unable to insert board: ' + error.message;
});
};
$scope.updateBoard = function(board) {
boardFactory.updateBoard(board)
.then(function (response) {
board = response.data;
}, function(error) {
$scope.status.createBoard = 'Unable to insert board: ' + error.message;
});
BoardService.update(board);
board.status.edit = false;
$scope.$apply();
};
$scope.deleteBoard = function(board) {
// TODO: Ask for confirmation
//if (confirm('Are you sure you want to delete this?')) {
BoardService.delete(board.id);
//}
};
$scope.selectColor = function(color) {
$scope.newBoard.color = color;
};
$scope.deleteBoard = function (index) {
var board = $scope.boards[index];
boardFactory.deleteBoard(board.id)
.then(function (response) {
$scope.status.deleteBoard = 'Deleted Board';
$scope.boards.splice( index, 1 );
}, function(error) {
$scope.status.deleteBoard = 'Unable to insert board: ' + error.message;
}]);
// OwnCloud Click Handling
// https://doc.owncloud.org/server/8.0/developer_manual/app/css.html
app.directive('appNavigationEntryUtils', function () {
'use strict';
return {
restrict: 'C',
link: function (scope, elm) {
var menu = elm.siblings('.app-navigation-entry-menu');
var button = $(elm)
.find('.app-navigation-entry-utils-menu-button button');
button.click(function () {
menu.toggleClass('open');
});
scope.$on('documentClicked', function (scope, event) {
if (event.target !== button[0]) {
menu.removeClass('open');
}
});
}
};
$scope.getBoards();
});
app.factory('ApiService', function($http, $q){
app.directive('autofocusOnInsert', function () {
'use strict';
return function (scope, elm) {
elm.focus();
};
});
app.factory('ApiService', ["$http", "$q", function($http, $q){
var ApiService = function(http, endpoint) {
this.endpoint = endpoint;
this.baseUrl = OC.generateUrl('/apps/deck/' + endpoint);
@@ -433,46 +395,37 @@ app.factory('ApiService', function($http, $q){
return ApiService;
});
}]);
app.factory('boardFactory', function($http){
var service = {};
var baseUrl = OC.generateUrl('/apps/deck/boards');
service.getBoards = function(){
return $http.get(baseUrl);
}
service.getBoard = function (id) {
board = $http.get(baseUrl + '/' + id);
return board;
};
service.createBoard = function (board) {
return $http.post(baseUrl, board);
};
service.updateBoard = function (board) {
return $http.put(baseUrl, board)
};
service.deleteBoard = function (id) {
return $http.delete(baseUrl + '/' + id);
};
return service;
});
app.factory('BoardService', function(ApiService, $http, $q){
app.factory('BoardService', ["ApiService", "$http", "$q", function(ApiService, $http, $q){
var BoardService = function($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
BoardService.prototype = angular.copy(ApiService.prototype);
service = new BoardService($http, 'boards', $q)
return service;
}]);
app.factory('CardService', ["ApiService", "$http", "$q", function(ApiService, $http, $q){
var CardService = function($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
CardService.prototype = angular.copy(ApiService.prototype);
CardService.prototype.reorder = function(card, order) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/reorder', {cardId: card.id, order: order, stackId: card.stackId}).then(function (response) {
card.order = order;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
app.factory('StackService', function(ApiService, $http, $q){
return deferred.promise;
}
service = new CardService($http, 'cards', $q)
return service;
}]);
app.factory('StackService', ["ApiService", "$http", "$q", function(ApiService, $http, $q){
var StackService = function($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
@@ -489,8 +442,52 @@ app.factory('StackService', function(ApiService, $http, $q){
return deferred.promise;
}
StackService.prototype.addCard = function(entity) {
console.log(this.data[entity.stackId]);
this.data[entity.stackId].cards.push(entity);
}
service = new StackService($http, 'stacks', $q)
return service;
}]);
app.service('StatusService', function(){
// Status Helper
this.active = true;
this.icon = 'loading';
this.title = 'Please wait';
this.text = 'Es dauert noch einen kleinen Moment';
this.counter = 2;
this.setStatus = function($icon, $title, $text) {
this.active = true;
this.icon = $icon;
this.title = $title;
this.text = $text;
}
this.setError = function($title, $text) {
this.active = true;
this.icon = 'error';
this.title = $title;
this.text = $text;
this.counter = 0;
}
this.releaseWaiting = function() {
if(this.counter>0)
this.counter--;
if(this.counter<=0) {
this.active = false;
this.counter = 0;
}
}
this.unsetStatus = function() {
this.active = false;
}
});

View File

@@ -0,0 +1,8 @@
app.factory('BoardService', function(ApiService, $http, $q){
var BoardService = function($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
BoardService.prototype = angular.copy(ApiService.prototype);
service = new BoardService($http, 'boards', $q)
return service;
});

20
js/service/CardService.js Normal file
View File

@@ -0,0 +1,20 @@
app.factory('CardService', function(ApiService, $http, $q){
var CardService = function($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
CardService.prototype = angular.copy(ApiService.prototype);
CardService.prototype.reorder = function(card, order) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/reorder', {cardId: card.id, order: order, stackId: card.stackId}).then(function (response) {
card.order = order;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while update ' + self.endpoint);
});
return deferred.promise;
}
service = new CardService($http, 'cards', $q)
return service;
});

View File

@@ -15,6 +15,11 @@ app.factory('StackService', function(ApiService, $http, $q){
return deferred.promise;
}
StackService.prototype.addCard = function(entity) {
console.log(this.data[entity.stackId]);
this.data[entity.stackId].cards.push(entity);
}
service = new StackService($http, 'stacks', $q)
return service;
});

View File

@@ -0,0 +1,38 @@
app.service('StatusService', function(){
// Status Helper
this.active = true;
this.icon = 'loading';
this.title = 'Please wait';
this.text = 'Es dauert noch einen kleinen Moment';
this.counter = 2;
this.setStatus = function($icon, $title, $text) {
this.active = true;
this.icon = $icon;
this.title = $title;
this.text = $text;
}
this.setError = function($title, $text) {
this.active = true;
this.icon = 'error';
this.title = $title;
this.text = $text;
this.counter = 0;
}
this.releaseWaiting = function() {
if(this.counter>0)
this.counter--;
if(this.counter<=0) {
this.active = false;
this.counter = 0;
}
}
this.unsetStatus = function() {
this.active = false;
}
});

View File

@@ -1,37 +0,0 @@
app.factory('boardFactory', function($http){
var service = {};
var baseUrl = OC.generateUrl('/apps/deck/boards');
service.getBoards = function(){
return $http.get(baseUrl);
}
service.getBoard = function (id) {
board = $http.get(baseUrl + '/' + id);
return board;
};
service.createBoard = function (board) {
return $http.post(baseUrl, board);
};
service.updateBoard = function (board) {
return $http.put(baseUrl, board)
};
service.deleteBoard = function (id) {
return $http.delete(baseUrl + '/' + id);
};
return service;
});
app.factory('BoardService', function(ApiService, $http, $q){
var BoardService = function($http, ep, $q) {
ApiService.call(this, $http, ep, $q);
};
BoardService.prototype = angular.copy(ApiService.prototype);
service = new BoardService($http, 'boards', $q)
return service;
});

73
service/cardservice.php Normal file
View File

@@ -0,0 +1,73 @@
<?php
namespace OCA\Deck\Service;
use OCP\ILogger;
use OCP\IL10N;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory;
use \OCA\Deck\Db\Card;
use \OCA\Deck\Db\CardMapper;
class CardService {
private $cardMapper;
private $logger;
public function __construct(CardMapper $cardMapper) {
$this->cardMapper = $cardMapper;
}
public function find($userId, $cardId) {
return $this->cardMapper->find($cardId);
}
public function create($title, $stackId, $type, $order, $owner) {
$card = new Card();
$card->setTitle($title);
$card->setStackId($stackId);
$card->setType($type);
$card->setOrder($order);
$card->setOwner($owner);
return $this->cardMapper->insert($card);
}
public function delete($userId, $id) {
return $this->cardMapper->delete($this->cardMapper->find($id));
}
public function update($id, $title, $stackId, $type, $order, $owner) {
$card = $this->cardMapper->find($id);
$card->setTitle($title);
$card->setStackId($stackId);
$card->setType($type);
$card->setOrder($order);
$card->setOwner($owner);
return $this->cardMapper->update($card);
}
public function reorder($id, $stackId, $order) {
$cards = $this->cardMapper->findAll($stackId);
$i = 0;
foreach ($cards as $card) {
if($card->id == $id) {
$card->setOrder($order);
}
if($i == $order)
$i++;
if($card->id !== $id) {
$card->setOrder($i++);
}
$this->cardMapper->update($card);
}
// FIXME: return reordered cards without an additional db query
$cards = $this->cardMapper->findAll($stackId);
return $cards;
}
}

View File

@@ -2,6 +2,7 @@
namespace OCA\Deck\Service;
use OCA\Deck\Db\CardMapper;
use OCP\ILogger;
use OCP\IL10N;
use OCP\AppFramework\Db\DoesNotExistException;
@@ -14,19 +15,25 @@ use \OCA\Deck\Db\StackMapper;
class StackService {
private $stackMapper;
private $cardMapper;
private $logger;
private $l10n;
private $timeFactory;
public function __construct(StackMapper $stackMapper, ILogger $logger,
public function __construct(StackMapper $stackMapper, CardMapper $cardMapper,ILogger $logger,
IL10N $l10n,
ITimeFactory $timeFactory) {
$this->stackMapper = $stackMapper;
$this->cardMapper = $cardMapper;
$this->logger = $logger;
}
public function findAll($boardId) {
return $this->stackMapper->findAll($boardId);
$stacks = $this->stackMapper->findAll($boardId);
foreach ($stacks as $idx => $s) {
$stacks[$idx]->setCards($this->cardMapper->findAll($s->id));
}
return $stacks;
}
public function create($title, $boardId, $order) {

View File

@@ -5,7 +5,7 @@ use OCP\Util;
Util::addStyle('deck', 'font-awesome');
Util::addStyle('deck', 'style');
Util::addStyle('deck', '../js/vendor/ng-sortable/dist/ng-sortable.min');
Util::addStyle('deck', '../js/vendor/ng-sortable/dist/ng-sortable.style.min');
//Util::addStyle('deck', '../js/vendor/ng-sortable/dist/ng-sortable.style.min');
Util::addScript('deck', 'vendor/angular/angular.min');
Util::addScript('deck', 'vendor/angular-route/angular-route.min');
Util::addScript('deck', 'vendor/angular-sanitize/angular-sanitize.min');

View File

@@ -1,11 +1,12 @@
<div id="board-status" ng-if="status.active">
<div id="board-status" ng-if="statusservice.active">
<div id="emptycontent">
<div class="icon-{{ status.icon }}"></div>
<h2>{{ status.title }}</h2>
<p>{{ status.text }}</p></div>
<div class="icon-{{ statusservice.icon }}"></div>
<h2>{{ statusservice.title }}</h2>
<p>{{ statusservice.text }}</p></div>
</div>
<div id="board" class="scroll-container" style="background-color:{{rgblight(boardservice.getCurrent().color)}};">
<h1 style="background-color:#{{ boardservice.getCurrent().color }};">{{ boardservice.data[id].title }}
<div id="board" class="scroll-container" >
<h1 style="border-bottom: 1px solid {{rgblight(boardservice.getCurrent().color)}};">
{{ boardservice.data[id].title }}
</h1>
<?php /* maybe later
<div class="board-actions">
@@ -15,7 +16,7 @@
</div> */ ?>
<div id="innerBoard" data-ng-model="stacks">
<div class="stack" ng-repeat="s in stackservice.data" data-columnindex="{{$index}}" id="column{{$index}}" data-ng-model="stackservice.data">
<div class="stack" ng-repeat="s in stackservice.data" data-columnindex="{{$index}}" id="column{{$index}}" data-ng-model="stackservice.data" style="border: 3px solid #{{ boardservice.getCurrent().color }};">
<h2><span ng-show="!s.status.editStack">{{ s.title }}</span>
<form ng-submit="stackservice.update(s)">
<input type="text" placeholder="Add a new stack" ng-blur="s.status.editStack=false" ng-model="s.title" ng-if="s.status.editStack" autofocus-on-insert/>
@@ -29,25 +30,24 @@
<ul data-as-sortable="sortOptions" data-ng-model="s.cards" style="min-height: 40px;">
<li class="card as-sortable-item" ng-repeat="c in s.cards" data-as-sortable-item>
<a href="#/board/{{ id }}/card/{{ c.id }}" data-as-sortable-item-handle>
<h3><i class="fa fa-github"></i> {{ c.title }}</h3>
<h3><!--<i class="fa fa-github"></i>//--> {{ c.title }}</h3>
<!--
<span class="info due"><i class="fa fa-clock-o" aria-hidden="true"></i> <span>Today</span></span>
<span class="info tasks"><i class="fa fa-list" aria-hidden="true"></i> <span>3/12</span></span>
<span class="info members"><i class="fa fa-users" aria-hidden="true"></i> <span>4</span></span>
//-->
<button class="icon-more"></button>
<ul class="labels">
<li style="color:#a00; border-color:#aa0000;">important</li>
<li style="color:#0a0; border-color:#00aa00;">action-needed</li>
<li style="color:#00a; border-color:#00a;">action-needed</li>
<li style="color:#ac8ac8; border-color:#ac8ac8;">action-needed</li>
</ul>
</a>
</li>
</ul>
<div class="card create" ng-click="s.status.addCard=!s.status.addCard">
<h3 ng-if="s.status.addCard" ><input type="text" autofocus-on-insert/></h3>
<i class="fa fa-plus" ></i>
<!-- CREATE CARD //-->
<div class="card create" style="background-color:#{{ boardservice.getCurrent().color }};">
<form ng-submit="createCard(s.id, newCard.title)">
<h3 ng-if="s.status.addCard" ><input type="text" autofocus-on-insert ng-model="newCard.title" ng-blur="s.status.addCard=false"/></h3>
</form>
<div class="fa fa-plus" ng-if="!s.status.addCard" ng-click="s.status.addCard=!s.status.addCard"></div>
</div>
</div>
<div class="stack" style="display: inline-block;">

View File

@@ -10,7 +10,7 @@
</tr>
</thead>
<tbody>
<tr data-ng-repeat="b in boards" ui-sref="board({boardId: b.id})">
<tr data-ng-repeat="b in boardservice.data" ui-sref="board({boardId: b.id})">
<td>
<span class="board-bullet" style="background-color:#{{b.color}};"> </span>
</td>

View File

@@ -1,6 +1,8 @@
<div id="card-header">
<h2>{{ cardId }} {{ card.title }}<a class="icon-close" ng-click="sidebar.show=!sidebar.show"> &nbsp;</a></h2>
<h2>{{ cardservice.getCurrent().title }}<a class="icon-close" ng-click="sidebar.show=!sidebar.show"> &nbsp;</a></h2>
Modified: {{ cardservice.getCurrent().modifiedAt }}
Created: {{ cardservice.getCurrent().createdAt }}
<ul class="labels">
<li style="color:#a00; border-color:#aa0000;">important</li>
<li style="color:#0a0; border-color:#00aa00;">action-needed</li>

View File

@@ -1,8 +1,12 @@
<ul class="with-icon">
<li><a href="#" class="">All Boards</a></li>
<!--<li><a href="#" class="icon-starred">Starred Boards</a></li>
<!--
<li><a href="#" class="icon-starred">Starred Boards</a></li>
<li><a href="#" class="icon-share">Shared Boards</a></li>
<li><a href="#" class="icon-public">Public Boards</a></li> //-->
<li><a href="#" class="icon-public">Public Boards</a></li>
//-->
<li class="with-menu" data-ng-repeat="b in boardservice.data">
<span class="board-bullet" style="background-color:#{{b.color}};" ng-if="!b.status.edit"> </span>
<a href="#/board/{{b.id}}" ng-if="!b.status.edit">{{ b.title }}</a>
@@ -15,7 +19,7 @@
<ul>
<li><button class="icon-share svg" title="share"></button></li>
<li><button class="icon-rename svg" title="rename" ng-click="b.status.edit=true"></button></li>
<li><button class="icon-delete svg" title="delete" ng-click="deleteBoard($index)"></button></li>
<li><button class="icon-delete svg" title="delete" ng-click="deleteBoard(b)"></button></li>
</ul>
</div>
<div class="app-navigation-entry-deleted" ng-show="false">
@@ -24,7 +28,7 @@
</div>
<div class="app-navigation-entry-edit" ng-show="b.status.edit">
<form ng-disabled="isAddingList" class="ng-pristine ng-valid" ng-submit="boardservice.update(b)">
<form ng-disabled="isAddingList" class="ng-pristine ng-valid" ng-submit="updateBoard(b)">
<input id="newTitle" class="edit ng-valid ng-empty" type="text" autofocus-on-insert ng-model="b.title">
<input type="submit" value="" class="action icon-checkmark svg">
<div class="colorselect">
@@ -34,7 +38,6 @@
</div>
</li>
<!-- Add new Board //-->
<li>
<a ng-click="status.addBoard=!status.addBoard" ng-show="!status.addBoard" class="icon-add">