This commit is contained in:
Julius Haertl
2016-06-20 10:44:41 +02:00
parent ba8283dcdf
commit c0a9f010a8
28 changed files with 691 additions and 106 deletions

View File

@@ -88,6 +88,12 @@
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>description</name>
<type>text</type>
<notnull>false</notnull>
<length>4096</length>
</field>
<field>
<name>stack_id</name>
<type>integer</type>
@@ -207,10 +213,10 @@
<notnull>false</notnull>
</field>
<field>
<name>owner</name>
<type>text</type>
<name>board_id</name>
<type>integer</type>
<notnull>true</notnull>
<length>64</length>
<length>8</length>
</field>
</declaration>
</table>

View File

@@ -5,7 +5,7 @@
<description>My first ownCloud app</description>
<licence>AGPL</licence>
<author>Julius Härtl</author>
<version>0.0.1.7</version>
<version>0.0.1.9</version>
<namespace>Deck</namespace>
<category>other</category>
<dependencies>

View File

@@ -37,6 +37,7 @@ return [
['name' => 'card#read', 'url' => '/cards/{cardId}/', 'verb' => 'GET'],
['name' => 'card#create', 'url' => '/cards/', 'verb' => 'POST'],
['name' => 'card#update', 'url' => '/cards/', 'verb' => 'PUT'],
['name' => 'card#rename', 'url' => '/cards/rename/', 'verb' => 'PUT'],
['name' => 'card#reorder', 'url' => '/cards/reorder/', 'verb' => 'PUT'],
['name' => 'card#delete', 'url' => '/cards/{cardId}/', 'verb' => 'DELETE'],

View File

@@ -1 +1 @@
0.0.1.4
0.0.1.5

View File

@@ -53,4 +53,8 @@ class BoardController extends Controller {
public function delete($boardId) {
return $this->boardService->delete($this->userId, $boardId);
}
public function labels($boardId) {
return $this->boardService->labels($this->boardId);
}
}

View File

@@ -35,6 +35,12 @@ class CardController extends Controller {
*/
public function reorder($cardId, $stackId, $order) {
return $this->cardService->reorder($cardId, $stackId, $order);
}
/**
* @NoAdminRequired
*/
public function rename($cardId, $title) {
return $this->cardService->rename($cardId, $title);
}
/**
* @NoAdminRequired
@@ -54,4 +60,6 @@ class CardController extends Controller {
public function delete($cardId) {
return $this->cardService->delete($this->userId, $cardId);
}
}

View File

@@ -45,14 +45,45 @@
color: #333333;
padding-right:250px;
z-index:100;
background-color:#ffffff;
background-color:#f7f7f7;
}
#board .board-actions {
#board-actions {
position:absolute;
right:5px;
top:5px;
z-index:999;
}
#board-actions .filter {
margin-left:10px;
margin-right:10px;
position:relative;
}
#board-actions .filter:hover {
color:#333333;
cursor: pointer;
}
.filter .filter-select {
position: absolute;
top: 42px;
right: -15px;
width: 100px;
}
.filter .filter-select li {
padding: 3px;
overflow: hidden;
width:auto;
}
.filter .filter-select li span {
display: block;
float: left;
width: 20px;height:20px;
margin-right:5px;
}
#board-actions div {
padding:5px;
float:left;
}
.board-actions button {
border: none;
background-color: transparent;
@@ -99,17 +130,34 @@
float:right;
}
.card {
background-color:#fafafa;
border: 1px solid #aaa;
background-color:#f6f6f6;
margin:5px;
padding:5px;
white-space: normal;
padding-bottom:4px;
overflow: hidden;
position: relative;
opacity: 1.0;
}
.card-upper {
overflow: hidden;
position: relative;
padding:5px;
}
.card .card-options {
position: absolute;
bottom: 10px;
right:10px;
}
.card .popovermenu {
z-index:999;
opacity: 1;
margin-left: 10px;
}
.card .card-assignees {
margin:5px;
}
.card:hover {
opacity: 0.6;
background-color:#fcfcfc;
}
.card a {
display:block;
@@ -119,13 +167,43 @@
font-size:10pt;
margin:0;
padding:0;
margin-bottom:20px;
margin-bottom:-5px;
margin-top:15px;
display: inline-block;
float:left;
}
.card h3 .fa {
font-size:18pt;
line-height:10pt;
vertical-align: middle;
}
.card .labels {
position: absolute;
top:-5px;
left:5px;
}
.card .labels li {
padding:0px;
width:15px;
height:20px;
-webkit-border-radius: 3px;
font-size:80%;
border-color: transparent;
border:none;
float:left;
}
.card .labels li span {
display: none;
}
.card .labels li:hover {
}
.card .labels li:hover span {
position:absolute;
padding:3px;
background-color: inherit;
}
.as-sortable-placeholder {
margin:5px;
@@ -133,23 +211,7 @@
border: 1px dashed #aaa;
}
.labels {
position:absolute;
top:0px;
margin-top:3px;
right:0px;
}
.labels li {
padding:3px;
line-height:100%;
color:white;
font-size:10px;
font-weight:600;
text-align:right;
padding-left:3px;
pdading-right:5px;
border-right:5px solid #aaaaaa;
}
.info {
padding-left:5px;
padding-right:5px;
@@ -166,7 +228,10 @@
.card.create {
text-align:center;
margin:0;
padding:0;
padding-top:4px;
border: none;
overflow:hidden;
}
.card.create:hover {
text-align:center;
@@ -175,6 +240,7 @@
.card.create h3 {
margin:0;
padding:0;
width: 100%;
}
.card.create h3 input {
width:100%;
@@ -188,7 +254,7 @@
border-bottom:1px solid #ffffff;
border-radius: 0px;
color: #ffffff;
background-color: transparent;
background-color: transparent !important;
}
.card.create .fa {
color:#ffffff;
@@ -213,12 +279,25 @@
}
#card-header h2 {
font-weight:600;
background-color: #f3f3f3;
padding:10px;
overflow: hidden;
margin-bottom:0px;
background-color:#f0f0f0;
}
#card-header h2 .icon-close {
float:right;
#card-header .icon-close {
position: absolute;
top:5px;
right:5px;
}
#card-meta {
padding-top:0px;
}
#card-dates {
font-size:80%;
color: #aaaaaa;
margin-bottom: 3px;
}
#card-dates span {
}
#card-description textarea {
@@ -250,10 +329,18 @@
#app-content.details-visible {
margin-right: 500px;
}
#card-header .labels {
float:right;
position:relative;
.labels {
display:block;
overflow:hidden;
}
.labels li {
padding:1px;
-webkit-border-radius: 3px;
margin:1px;
float:left;
color: #ffffff;
font-size:80%;
font-weight:900;
}
#assigned-users {
padding:10px;
@@ -313,3 +400,28 @@
float:left;
}
#boardlist .colorselect {margin-top:5px;}
input.input-inline {
font-size: inherit !important;
font-weight: inherit;
background-color:transparent;
padding:0;
margin:0;
border:none;
width:100%;
border-bottom:1px solid #333333;
-webkit-border-radius: 0;
margin-top:-4px;
line-height:100%;
margin-bottom: -4px;
}
button {
border:0;
background-color: transparent;
}
button:hover {
border:0;
background-color: transparent;
}

View File

@@ -12,6 +12,7 @@ class Board extends Entity implements JsonSerializable {
protected $owner;
protected $color;
protected $archived;
protected $labels;
public function __construct() {
$this->addType('id','integer');
}
@@ -20,7 +21,8 @@ class Board extends Entity implements JsonSerializable {
'id' => $this->id,
'title' => $this->title,
'owner' => $this->owner,
'color' => $this->color
'color' => $this->color,
'labels' => $this->labels,
];
}
}

View File

@@ -9,8 +9,11 @@ use OCP\AppFramework\Db\Mapper;
class BoardMapper extends Mapper {
public function __construct(IDb $db) {
private $labelMapper;
public function __construct(IDb $db, LabelMapper $labelMapper) {
parent::__construct($db, 'deck_boards', '\OCA\Deck\Db\Board');
$this->labelMapper = $labelMapper;
}
@@ -21,7 +24,10 @@ class BoardMapper extends Mapper {
public function find($id) {
$sql = 'SELECT * FROM `*PREFIX*deck_boards` ' .
'WHERE `id` = ?';
return $this->findEntity($sql, [$id]);
$board = $this->findEntity($sql, [$id]);
$labels = $this->labelMapper->findAll($id);
$board->setLabels($labels);
return $board;
}

View File

@@ -9,10 +9,12 @@ class Card extends Entity implements JsonSerializable {
public $id;
protected $title;
protected $description;
protected $stackId;
protected $type;
protected $lastModified;
protected $createdAt;
protected $labels;
protected $owner;
protected $order;
public function __construct() {
@@ -25,12 +27,14 @@ class Card extends Entity implements JsonSerializable {
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'type' => $this->type,
'lastModified' => $this->lastModified,
'createdAt' => $this->createdAt,
'owner' => $this->owner,
'order' => $this->order,
'stackId' => $this->stackId,
'labels' => $this->labels,
];
}
}

View File

@@ -14,6 +14,21 @@ class CardMapper extends Mapper {
parent::__construct($db, 'deck_cards', '\OCA\Deck\Db\Card');
}
public function insert(Entity $entity) {
$entity->setCreatedAt(time());
$entity->setLastModified(time());
return parent::insert($entity);
}
/**
* @param Entity $entity
* @return Entity
*/
public function update(Entity $entity) {
$entity->setLastModified(time());
return parent::update($entity);
}
/**
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
@@ -25,10 +40,14 @@ class CardMapper extends Mapper {
return $this->findEntity($sql, [$id]);
}
public function findAllByBoard($boardId, $limit=null, $offset=null) {
}
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);
$entities = $this->findEntities($sql, [$stackId], $limit, $offset);
return $entities;
}
public function delete(Entity $entity) {

32
db/deckmapper.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace OCA\Deck\Db;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\Mapper;
abstract class DeckMapper extends Mapper {
/**
* @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 `' . $this->tableName . '` ' . 'WHERE `id` = ?';
return $this->findEntity($sql, [$id]);
}
/**
* Add relational data to an Entity by calling the related Mapper
* @param $entities
* @param $entityType
* @param $property
* addRelation($cards, $labels, function($one, $many) {
* if($one->id == $many->cardId)
* }
*/
public function addRelation($entities, $entityType, $property) {
}
}

27
db/label.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
// db/author.php
namespace OCA\Deck\Db;
use JsonSerializable;
use OCP\AppFramework\Db\Entity;
class Label extends Entity implements JsonSerializable {
public $id;
protected $title;
protected $color;
protected $boardId;
protected $cardId;
public function __construct() {
$this->addType('id','integer');
}
public function jsonSerialize() {
return [
'id' => $this->id,
'title' => $this->title,
'boardId' => $this->boardId,
'cardId' => $this->cardId,
'color' => $this->color,
];
}
}

48
db/labelmapper.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
namespace OCA\Deck\Db;
use OCP\AppFramework\Db\Entity;
use OCP\IDb;
use OCP\AppFramework\Db\Mapper;
class LabelMapper extends DeckMapper {
public function __construct(IDb $db) {
parent::__construct($db, 'deck_labels', '\OCA\Deck\Db\Label');
}
public function findAll($boardId, $limit=null, $offset=null) {
$sql = 'SELECT * FROM `*PREFIX*deck_labels` WHERE `board_id` = ?';
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);
}
public function findAssignedLabelsForCard($cardId) {
$sql = 'SELECT * FROM `*PREFIX*deck_assigned_labels` as al JOIN *PREFIX*deck_labels as l ON l.id = al.label_id WHERE `card_id` = ?';
return $this->findEntities($sql, [$cardId], $limit, $offset);
}
public function findAssignedLabelsForBoard($boardId, $limit=null, $offset=null) {
$sql = "SELECT c.id as card_id, l.id as id, l.title as title, color FROM oc_deck_cards as c " .
"JOIN oc_deck_assigned_labels as al, oc_deck_labels as l ON al.card_id = c.id AND al.label_id = l.id WHERE board_id=?";
$entities = $this->findEntities($sql, [$boardId], $limit, $offset);
return $entities;
}
public function getAssignedLabelsForBoard($boardId) {
$labels = $this->findAssignedLabelsForBoard($boardId);
$result = array();
foreach ($labels as $label) {
if(!is_array($result[$label->getCardId()])) {
$result[$label->getCardId()] = array();
}
$result[$label->getCardId()][] = $label;
}
return $result;
}
}

View File

@@ -7,18 +7,25 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
$scope.stackservice = StackService;
$scope.boardservice = BoardService;
$scope.statusservice = StatusService;
$scope.statusservice = StatusService.getInstance();
// fetch data
StackService.clear();
$scope.statusservice.retainWaiting();
$scope.statusservice.retainWaiting();
console.log("foo");
StackService.fetchAll($scope.id).then(function(data) {
console.log(data);
$scope.statusservice.releaseWaiting();
}, function(error) {
$scope.statusservice.setError('Error occured', error);
});
BoardService.fetchOne($scope.id).then(function(data) {
$scope.statusservice.releaseWaiting();
}, function(error) {
$scope.statusservice.setError('Error occured', error);
@@ -48,6 +55,12 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St
});
}
$scope.cardDelete = function(card) {
CardService.delete(card.id);
StackService.deleteCard(card);
}
// 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);

View File

@@ -1,17 +1,31 @@
app.controller('CardController', function ($scope, $rootScope, $routeParams, $location, $stateParams, CardService) {
app.controller('CardController', function ($scope, $rootScope, $routeParams, $location, $stateParams, BoardService, CardService, StackService, StatusService) {
$scope.sidebar = $rootScope.sidebar;
$scope.cardservice = CardService;
$scope.cardId = $stateParams.cardId;
$scope.statusservice = StatusService.getInstance();
$scope.boardservice = BoardService;
$scope.statusservice.retainWaiting();
CardService.fetchOne($scope.cardId).then(function(data) {
$scope.statusservice.releaseWaiting();
console.log(data);
}, function(error) {
});
// handle rename to update information on the board as well
$scope.renameCard = function(card) {
CardService.rename(card).then(function(data) {
StackService.updateCard(card);
$scope.status.renameCard = false;
});
};
/*var menu = $('#app-content');

18
js/directive/avatar.js Normal file
View File

@@ -0,0 +1,18 @@
app.directive('avatar', function() {
'use strict';
return {
restrict: 'A',
scope: false,
link: function(scope, elm, attr) {
return attr.$observe('user', function() {
if (attr.user) {
var url = OC.generateUrl('/avatar/{user}/{size}',
{user: attr.user, size: Math.ceil(attr.size * window.devicePixelRatio)});
var inner = '<img src="'+url+'" />';
elm.html(inner);
//elm.avatar(attr.user, attr.size);
}
});
}
};
});

View File

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

View File

@@ -69,18 +69,25 @@ app.controller('BoardController', ["$rootScope", "$scope", "$stateParams", "Stat
$scope.stackservice = StackService;
$scope.boardservice = BoardService;
$scope.statusservice = StatusService;
$scope.statusservice = StatusService.getInstance();
// fetch data
StackService.clear();
$scope.statusservice.retainWaiting();
$scope.statusservice.retainWaiting();
console.log("foo");
StackService.fetchAll($scope.id).then(function(data) {
console.log(data);
$scope.statusservice.releaseWaiting();
}, function(error) {
$scope.statusservice.setError('Error occured', error);
});
BoardService.fetchOne($scope.id).then(function(data) {
$scope.statusservice.releaseWaiting();
}, function(error) {
$scope.statusservice.setError('Error occured', error);
@@ -110,6 +117,12 @@ app.controller('BoardController', ["$rootScope", "$scope", "$stateParams", "Stat
});
}
$scope.cardDelete = function(card) {
CardService.delete(card.id);
StackService.deleteCard(card);
}
// 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);
@@ -176,18 +189,32 @@ app.controller('BoardController', ["$rootScope", "$scope", "$stateParams", "Stat
app.controller('CardController', ["$scope", "$rootScope", "$routeParams", "$location", "$stateParams", "CardService", function ($scope, $rootScope, $routeParams, $location, $stateParams, CardService) {
app.controller('CardController', ["$scope", "$rootScope", "$routeParams", "$location", "$stateParams", "BoardService", "CardService", "StackService", "StatusService", function ($scope, $rootScope, $routeParams, $location, $stateParams, BoardService, CardService, StackService, StatusService) {
$scope.sidebar = $rootScope.sidebar;
$scope.cardservice = CardService;
$scope.cardId = $stateParams.cardId;
$scope.statusservice = StatusService.getInstance();
$scope.boardservice = BoardService;
$scope.statusservice.retainWaiting();
CardService.fetchOne($scope.cardId).then(function(data) {
$scope.statusservice.releaseWaiting();
console.log(data);
}, function(error) {
});
// handle rename to update information on the board as well
$scope.renameCard = function(card) {
CardService.rename(card).then(function(data) {
StackService.updateCard(card);
$scope.status.renameCard = false;
});
};
/*var menu = $('#app-content');
@@ -270,6 +297,54 @@ app.directive('autofocusOnInsert', function () {
elm.focus();
};
});
app.directive('avatar', function() {
'use strict';
return {
restrict: 'A',
scope: false,
link: function(scope, elm, attr) {
return attr.$observe('user', function() {
if (attr.user) {
var url = OC.generateUrl('/avatar/{user}/{size}',
{user: attr.user, size: Math.ceil(attr.size * window.devicePixelRatio)});
var inner = '<img src="'+url+'" />';
elm.html(inner);
//elm.avatar(attr.user, attr.size);
}
});
}
};
});
// OwnCloud Click Handling
// https://doc.owncloud.org/server/8.0/developer_manual/app/css.html
app.directive('cardActionUtils', function () {
'use strict';
return {
restrict: 'C',
scope: {
ngModel : '=',
},
link: function (scope, elm) {
console.log(scope);
/*
var menu = elm.siblings('.popovermenu');
var button = $(elm)
.find('li a');
button.click(function () {
menu.toggleClass('open');
});
scope.$on('documentClicked', function (scope, event) {
if (event.target !== button[0]) {
menu.removeClass('open');
}
});
*/
}
};
});
app.factory('ApiService', ["$http", "$q", function($http, $q){
var ApiService = function(http, endpoint) {
this.endpoint = endpoint;
@@ -298,8 +373,14 @@ app.factory('ApiService', ["$http", "$q", function($http, $q){
}
ApiService.prototype.fetchOne = function (id) {
this.id = id;
var deferred = $q.defer();
if(id===undefined) {
return deferred.promise;
}
var self = this;
$http.get(this.baseUrl + '/' + id).then(function (response) {
data = response.data;
@@ -422,6 +503,19 @@ app.factory('CardService', ["ApiService", "$http", "$q", function(ApiService, $h
});
return deferred.promise;
}
CardService.prototype.rename = function(card) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/rename', {cardId: card.id, title: card.title}).then(function (response) {
self.data[card.id].title = card.title;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while renaming ' + self.endpoint);
});
return deferred.promise;
}
service = new CardService($http, 'cards', $q)
return service;
}]);
@@ -444,30 +538,50 @@ app.factory('StackService', ["ApiService", "$http", "$q", function(ApiService, $
}
StackService.prototype.addCard = function(entity) {
console.log(this.data[entity.stackId]);
this.data[entity.stackId].cards.push(entity);
}
service = new StackService($http, 'stacks', $q)
StackService.prototype.updateCard = function(entity) {
var self = this;
var cards = this.data[entity.stackId].cards;
for(var i=0;i<cards.length;i++) {
if(cards[i].id == entity.id) {
cards[i] = entity;
}
}
}
StackService.prototype.deleteCard = function(entity) {
var self = this;
var cards = this.data[entity.stackId].cards;
for(var i=0;i<cards.length;i++) {
if(cards[i].id == entity.id) {
cards.splice(i, 1);
}
}
}
service = new StackService($http, 'stacks', $q);
return service;
}]);
app.service('StatusService', function(){
app.factory('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;
var StatusService = function() {
this.active = true;
this.icon = 'loading';
this.title = 'Please wait';
this.text = 'Es dauert noch einen kleinen Moment';
this.counter = 0;
}
this.setStatus = function($icon, $title, $text) {
StatusService.prototype.setStatus = function($icon, $title, $text) {
this.active = true;
this.icon = $icon;
this.title = $title;
this.text = $text;
}
this.setError = function($title, $text) {
StatusService.prototype.setError = function($title, $text) {
this.active = true;
this.icon = 'error';
this.title = $title;
@@ -475,7 +589,7 @@ app.service('StatusService', function(){
this.counter = 0;
}
this.releaseWaiting = function() {
StatusService.prototype.releaseWaiting = function() {
if(this.counter>0)
this.counter--;
if(this.counter<=0) {
@@ -484,10 +598,25 @@ app.service('StatusService', function(){
}
}
this.unsetStatus = function() {
StatusService.prototype.retainWaiting = function() {
this.active = true;
this.icon = 'loading';
this.title = 'Please wait';
this.text = 'Es dauert noch einen kleinen Moment';
this.counter++;
}
StatusService.prototype.unsetStatus = function() {
this.active = false;
}
return {
getInstance: function() {
return new StatusService();
}
}
});

View File

@@ -26,8 +26,14 @@ app.factory('ApiService', function($http, $q){
}
ApiService.prototype.fetchOne = function (id) {
this.id = id;
var deferred = $q.defer();
if(id===undefined) {
return deferred.promise;
}
var self = this;
$http.get(this.baseUrl + '/' + id).then(function (response) {
data = response.data;

View File

@@ -15,6 +15,19 @@ app.factory('CardService', function(ApiService, $http, $q){
});
return deferred.promise;
}
CardService.prototype.rename = function(card) {
var deferred = $q.defer();
var self = this;
$http.put(this.baseUrl + '/rename', {cardId: card.id, title: card.title}).then(function (response) {
self.data[card.id].title = card.title;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while renaming ' + self.endpoint);
});
return deferred.promise;
}
service = new CardService($http, 'cards', $q)
return service;
});

View File

@@ -17,10 +17,27 @@ app.factory('StackService', function(ApiService, $http, $q){
}
StackService.prototype.addCard = function(entity) {
console.log(this.data[entity.stackId]);
this.data[entity.stackId].cards.push(entity);
}
service = new StackService($http, 'stacks', $q)
StackService.prototype.updateCard = function(entity) {
var self = this;
var cards = this.data[entity.stackId].cards;
for(var i=0;i<cards.length;i++) {
if(cards[i].id == entity.id) {
cards[i] = entity;
}
}
}
StackService.prototype.deleteCard = function(entity) {
var self = this;
var cards = this.data[entity.stackId].cards;
for(var i=0;i<cards.length;i++) {
if(cards[i].id == entity.id) {
cards.splice(i, 1);
}
}
}
service = new StackService($http, 'stacks', $q);
return service;
});

View File

@@ -1,19 +1,22 @@
app.service('StatusService', function(){
app.factory('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;
var StatusService = function() {
this.active = true;
this.icon = 'loading';
this.title = 'Please wait';
this.text = 'Es dauert noch einen kleinen Moment';
this.counter = 0;
}
this.setStatus = function($icon, $title, $text) {
StatusService.prototype.setStatus = function($icon, $title, $text) {
this.active = true;
this.icon = $icon;
this.title = $title;
this.text = $text;
}
this.setError = function($title, $text) {
StatusService.prototype.setError = function($title, $text) {
this.active = true;
this.icon = 'error';
this.title = $title;
@@ -21,7 +24,7 @@ app.service('StatusService', function(){
this.counter = 0;
}
this.releaseWaiting = function() {
StatusService.prototype.releaseWaiting = function() {
if(this.counter>0)
this.counter--;
if(this.counter<=0) {
@@ -30,9 +33,24 @@ app.service('StatusService', function(){
}
}
this.unsetStatus = function() {
StatusService.prototype.retainWaiting = function() {
this.active = true;
this.icon = 'loading';
this.title = 'Please wait';
this.text = 'Es dauert noch einen kleinen Moment';
this.counter++;
}
StatusService.prototype.unsetStatus = function() {
this.active = false;
}
return {
getInstance: function() {
return new StatusService();
}
}
});

View File

@@ -59,4 +59,8 @@ class BoardService {
$board->setColor($color);
return $this->boardMapper->update($board);
}
public function labels($boardId) {
}
}

View File

@@ -49,6 +49,11 @@ class CardService {
return $this->cardMapper->update($card);
}
public function rename($id, $title) {
$card = $this->cardMapper->find($id);
$card->setTitle($title);
return $this->cardMapper->update($card);
}
public function reorder($id, $stackId, $order) {
$cards = $this->cardMapper->findAll($stackId);
$i = 0;

View File

@@ -3,12 +3,14 @@
namespace OCA\Deck\Service;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\LabelMapper;
use OCP\ILogger;
use OCP\IL10N;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory;
use \OCA\Deck\Db\Stack;
use \OCA\Deck\Db\StackMapper;
@@ -19,19 +21,27 @@ class StackService {
private $logger;
private $l10n;
private $timeFactory;
private $labelMapper;
public function __construct(StackMapper $stackMapper, CardMapper $cardMapper,ILogger $logger,
public function __construct(StackMapper $stackMapper, CardMapper $cardMapper, LabelMapper $labelMapper, ILogger $logger,
IL10N $l10n,
ITimeFactory $timeFactory) {
$this->stackMapper = $stackMapper;
$this->cardMapper = $cardMapper;
$this->labelMapper = $labelMapper;
$this->logger = $logger;
}
public function findAll($boardId) {
$stacks = $this->stackMapper->findAll($boardId);
$labels = $this->labelMapper->getAssignedLabelsForBoard($boardId);
foreach ($stacks as $idx => $s) {
$stacks[$idx]->setCards($this->cardMapper->findAll($s->id));
$cards = $this->cardMapper->findAll($s->id);
foreach ($cards as $idxc => $card) {
$cards[$idxc]->setLabels($labels[$card->id]);
}
$stacks[$idx]->setCards($cards);
}
return $stacks;
}

View File

@@ -5,21 +5,32 @@
<p>{{ statusservice.text }}</p></div>
</div>
<div id="board" class="scroll-container" >
<h1 style="border-bottom: 1px solid {{rgblight(boardservice.getCurrent().color)}};">
<h1>
{{ boardservice.data[id].title }}
</h1>
<?php /* maybe later
<div class="board-actions">
<button class="fa fa-share-alt"></button>
<button class="fa fa-users"></button>
<button class="fa fa-ellipsis-h"></button>
</div> */ ?>
<div id="board-actions">
<div><i class="fa fa-filter"> </i> Filter</div>
<div class="filter">by label <i class="fa fa-caret-down"> </i>
<ul class="filter-select bubble">
<li ng-repeat="label in boardservice.data[id].labels"><span style="background-color:#{{ label.color }};"> </span> {{ label.title }}</li>
</ul>
</div>
<div class="filter">by creator <i class="fa fa-caret-down"> </i></div>
<div class="filter">by members <i class="fa fa-caret-down"> </i></div>
<div><i class="fa fa-share-alt"> </i></div>
<div><i class="fa fa-users"> </i></div>
<div><i class="fa fa-ellipsis-h"> </i></div>
</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" style="border: 3px solid #{{ boardservice.getCurrent().color }};">
<div class="stack" ng-repeat="s in stackservice.data" data-columnindex="{{$index}}" id="column{{$index}}" data-ng-model="stackservice.data" style="border: 5px 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/>
<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 required />
<button class="icon icon-save" ng-if="s.status.editStack" type="submit"></button>
</form>
<div class="stack-actions">
@@ -28,24 +39,39 @@
</div>
</h2>
<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>
<!--
<span class="info due"><i class="fa fa-clock-o" aria-hidden="true"></i> <span>Today</span></span>
<li class="card as-sortable-item" ng-repeat="c in s.cards" data-as-sortable-item ui-sref="board.card({boardId: id, cardId: c.id})">
<div data-as-sortable-item-handle>
<div class="card-upper">
<h3>{{ c.title }}</h3>
<ul class="labels">
<li ng-repeat="label in c.labels" style="background-color: #{{ label.color }};"><span>{{ label.title }}</span></li>
</ul>
</div>
<button class="card-options icon-more" ng-click="c.status.showMenu=!c.status.showMenu; $event.stopPropagation();" ng-model="card"></button>
<div class="popovermenu bubble" ng-show="c.status.showMenu"><ul>
<li><a class="menuitem action action-rename permanent" data-action="Rename"><span class="icon icon-rename"></span><span>Umbenennen</span></a></li>
<li><a class="menuitem action action-rename permanent" data-action="Rename"><span class="icon icon-rename"></span><span>Archive</span></a></li>
<li><a class="menuitem action action-delete permanent" data-action="Delete" ng-click="cardDelete(c)"><span class="icon icon-delete"></span><span>Löschen</span></a></li></ul>
</div>
<div class="card-assignees">
<!-- <div class="avatar" avatar user="{{c.owner}}" size="24"></div>//-->
</div>
<!--<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>
</a>
</div>
</li>
</ul>
<!-- 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>
<h3 ng-if="s.status.addCard" >
<input type="text" autofocus-on-insert ng-model="newCard.title" ng-blur="s.status.addCard=false" required />
</h3>
</form>
<div class="fa fa-plus" ng-if="!s.status.addCard" ng-click="s.status.addCard=!s.status.addCard"></div>
</div>
@@ -53,7 +79,7 @@
<div class="stack" style="display: inline-block;">
<form class="ng-pristine ng-valid" ng-submit="createStack()">
<h2>
<input type="text" placeholder="Add a new stack" ng-focus="status.addStack=true" ng-blur="status.addStack=false" ng-model="newStack.title" >
<input type="text" placeholder="Add a new stack" ng-focus="status.addStack=true" ng-blur="status.addStack=false" ng-model="newStack.title" required />
<button class="icon icon-add" ng-show="status.addStack" type="submit"></button>
</h2>
</form>

View File

@@ -1,14 +1,29 @@
<div id="board-status" ng-if="statusservice.active">
<div id="emptycontent">
<div class="icon-{{ statusservice.icon }}"></div>
<h2>{{ statusservice.title }}</h2>
<p>{{ statusservice.text }}</p></div>
</div>
<div id="card-header">
<a class="icon-close" ui-sref="board" ng-click="sidebar.show=!sidebar.show"> &nbsp;</a>
<h2>
<form ng-submit="renameCard(cardservice.getCurrent())">
<input class="input-inline" type="text" ng-if="status.renameCard" ng-model="cardservice.getCurrent().title" ng-blur="renameCard(cardservice.getCurrent())" autofocus-on-insert required>
</form>
<div ng-click="status.renameCard=true" ng-show="!status.renameCard">{{ cardservice.getCurrent().title }}</div>
</h2>
</div>
<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>
<li style="color:#00a; border-color:#00a;">action-needed</li>
<li style="color:#ac8ac8; border-color:#ac8ac8;">action-needed</li>
</ul>
<div id="card-meta" class="card-block">
<div id="card-dates">
Modified: <span>{{ cardservice.getCurrent().lastModified*1000|date:'medium' }}</span>
Created: <span>{{ cardservice.getCurrent().createdAt*1000|date:'medium' }}</span>
</div>
<ul class="labels">
<li style="background-color:#aa0000;">important</li>
<li style="background-color:#00aa00;">action-needed</li>
<li style="background-color:#00a;">action-needed</li>
</ul>
<div id="assigned-users">
<div class="avatardiv" style="height: 30px; width: 30px; color: rgb(255, 255, 255); font-weight: normal; text-align: center; line-height: 30px; font-size: 17px; background-color: rgb(213, 231, 116);">D</div>
<div class="avatardiv" style="height: 30px; width: 30px; color: rgb(255, 255, 255); font-weight: normal; text-align: center; line-height: 30px; font-size: 17px; background-color: rgb(213, 120, 220);">E</div>
@@ -18,13 +33,12 @@
</div>
<div id="card-description">
<textarea>
{{ card.description }}
</textarea>
<textarea ng-model="cardservice.getCurrent().description">{{ cardservice.getCurrent().description }}</textarea>
<div class="saved">Saved</div>
</div>
</div>
<!--
<div id="card-attachments">
<h3>Attachments</h3>
<ul>
@@ -59,4 +73,4 @@
Quia quia qui aspernatur cumque quo omnis corporis. Reprehenderit id sint architecto magni in. Et harum sequi eaque quasi qui sed id quod.
Officia quaerat facere et totam officiis dolores velit qui. Earum velit sint quia. Id libero quibusdam voluptatem.
</p>
</div>
</div>// -->