Commit e4a9ea51 authored by Peter Harrison's avatar Peter Harrison Committed by GitHub

Merge pull request #53 from cheetah100/development

Pulling latest changes into Master
parents ec60a707 d003ce0d
......@@ -4,7 +4,7 @@
<groupId>nz.net.gravity</groupId>
<artifactId>gravity</artifactId>
<packaging>war</packaging>
<version>3.0.1-SNAPSHOT</version>
<version>3.0.2-SNAPSHOT</version>
<name>Gravity Workflow Automation</name>
<properties>
......@@ -15,6 +15,7 @@
<java.version>1.6</java.version>
<junit.version>4.8.2</junit.version>
<jackson.version>1.9.10</jackson.version>
<camel.version>2.17.2</camel.version>
</properties>
<licenses>
......@@ -60,17 +61,17 @@
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
<version>1.1.0.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.0.GA</version>
<version>5.2.4.Final</version>
<scope>compile</scope>
</dependency>
<!-- Spring Dependencies -->
<dependency>
......@@ -343,7 +344,6 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
......@@ -398,6 +398,20 @@
<artifactId>sjsxp</artifactId>
<version>1.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.camel/camel-core -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>${camel.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.camel/camel-spring -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
<version>${camel.version}</version>
</dependency>
</dependencies>
......
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
* (C) Copyright 2016 Peter Harrison
*
* This file is part of Gravity Workflow Automation.
*
......@@ -57,7 +58,7 @@ public class CacheManager implements MessageListener, CacheInvalidationInterface
CacheInvalidationInstruction instruction = (CacheInvalidationInstruction) objectMessage.getObject();
if( logger.isDebugEnabled()){
logger.debug( "Cache Invalidation Instruction: "
logger.debug( "Cache Invalidation Instruction Received: "
+ instruction.getCacheType() + " " + instruction.getId());
}
......@@ -78,6 +79,9 @@ public class CacheManager implements MessageListener, CacheInvalidationInterface
CacheInvalidationInstruction instruction =
new CacheInvalidationInstruction(type,id);
this.jmsTemplate.convertAndSend(instruction);
if( logger.isDebugEnabled()){
logger.debug("Invalidation Message Sent to Queue: " + type + " - " + id);
}
}
public void setTimerManager(TimerManager timerManager) {
......
......@@ -47,6 +47,7 @@ import nz.net.orcon.kanban.tools.IdentifierTools;
import nz.net.orcon.kanban.tools.ListTools;
import nz.net.orcon.kanban.tools.OcmMapperFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.ocm.manager.ObjectContentManager;
import org.apache.jackrabbit.ocm.query.Query;
import org.apache.jackrabbit.ocm.query.QueryManager;
......@@ -62,8 +63,6 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.amazonaws.util.StringUtils;
/**
*
*/
......@@ -284,15 +283,12 @@ public class BoardController {
@RequestMapping(value = "/{boardId}", method=RequestMethod.DELETE)
public @ResponseBody void deleteBoard(@PathVariable String boardId) throws Exception {
throw new Exception("Method Unavailable!");
/*
if( StringUtils.isBlank(boardId)){
if( StringUtils.isEmpty(boardId)){
return;
}
ObjectContentManager ocm = ocmFactory.getOcm();
Node node = ocm.getSession().getNode(String.format(boardUri, boardId));
Node node = ocm.getSession().getNode(String.format(URI.BOARD_URI, boardId));
if(node==null){
ocm.logout();
......@@ -302,8 +298,7 @@ public class BoardController {
node.remove();
ocm.save();
ocm.logout();
this.cacheInvalidationManager.invalidate(CacheType.BOARD, boardId);
*/
this.cacheInvalidationManager.invalidate(BOARD, boardId);
}
......@@ -409,11 +404,11 @@ public class BoardController {
detailFilter = "(jcr:contains(@detail,'${detail}'))".replaceAll("\\$\\{detail\\}", detail);
}
if( !StringUtils.isNullOrEmpty(categoryFilter) && !StringUtils.isNullOrEmpty(detailFilter)){
if( !StringUtils.isEmpty(categoryFilter) && !StringUtils.isEmpty(detailFilter)){
qmFilter.addJCRExpression( categoryFilter + " or " + detailFilter);
} else if ( !StringUtils.isNullOrEmpty(categoryFilter) && StringUtils.isNullOrEmpty(detailFilter)) {
} else if ( !StringUtils.isEmpty(categoryFilter) && StringUtils.isEmpty(detailFilter)) {
qmFilter.addJCRExpression( categoryFilter );
} else if (StringUtils.isNullOrEmpty(categoryFilter) && !StringUtils.isNullOrEmpty(detailFilter)) {
} else if (StringUtils.isEmpty(categoryFilter) && !StringUtils.isEmpty(detailFilter)) {
qmFilter.addJCRExpression( detailFilter );
}
......@@ -468,8 +463,8 @@ public class BoardController {
detailFilter = "(jcr:contains(@detail,'${detail}'))".replaceAll("\\$\\{detail\\}", detail);
}
boolean categoryPresent = !StringUtils.isNullOrEmpty(categoryFilter);
boolean detailPresent = !StringUtils.isNullOrEmpty(detailFilter);
boolean categoryPresent = !StringUtils.isEmpty(categoryFilter);
boolean detailPresent = !StringUtils.isEmpty(detailFilter);
if(categoryPresent && detailPresent){
qmFilter.addJCRExpression( categoryFilter + " or " + detailFilter);
......
......@@ -26,9 +26,15 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
abstract public class CacheImpl<T> implements Cache<T> {
private static final Logger logger = LoggerFactory.getLogger(CacheImpl.class);
private static String DEFAULT_LIST_NAME = "default";
private Map<String, T> cacheMap = new ConcurrentHashMap<String,T>();
......@@ -44,7 +50,18 @@ abstract public class CacheImpl<T> implements Cache<T> {
for( int a=0; a<itemIds.length-1; a++){
listId[a] = itemIds[a];
}
this.cacheList.remove(getCacheId(listId));
String cacheListId = getCacheId(listId);
this.cacheList.remove(cacheListId);
if(logger.isDebugEnabled()){
logger.debug("Clearing List: " + cacheListId);
}
} else if(itemIds.length==1){
this.cacheList.remove(DEFAULT_LIST_NAME);
if(logger.isDebugEnabled()){
logger.debug("Clearing Root List: " + itemIds[0]);
}
}
}
......@@ -68,7 +85,7 @@ abstract public class CacheImpl<T> implements Cache<T> {
}
public Map<String,String> list(String... prefixs) throws Exception{
String cacheId = "default";
String cacheId = DEFAULT_LIST_NAME;
if(prefixs.length>0){
cacheId = this.getCacheId(prefixs);
}
......@@ -83,20 +100,6 @@ abstract public class CacheImpl<T> implements Cache<T> {
}
return new HashMap<String,String>(this.cacheList.get(cacheId));
/*
if( this.cacheList == null){
if( prefixs.length>0){
cacheId = this.getCacheId(prefixs);
Map<String,String> list = this.getListFromStore(prefixs);
this.cacheList.put(cacheId, list);
} else {
Map<String,String> list = this.getListFromStore("");
this.cacheList.put(cacheId, list);
}
}
return new HashMap<String,String>(this.cacheList.get(cacheId));
*/
}
@Override
......
......@@ -22,7 +22,9 @@
package nz.net.orcon.kanban.controllers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
......@@ -30,7 +32,9 @@ import javax.annotation.Resource;
import javax.jcr.Node;
import nz.net.orcon.kanban.automation.CacheInvalidationInterface;
import nz.net.orcon.kanban.model.Card;
import nz.net.orcon.kanban.model.ListResource;
import nz.net.orcon.kanban.model.ListResourceType;
import nz.net.orcon.kanban.model.Option;
import nz.net.orcon.kanban.tools.IdentifierTools;
import nz.net.orcon.kanban.tools.ListTools;
......@@ -68,6 +72,12 @@ public class ListController {
@Autowired
ListTools listTools;
@Autowired
PhaseController phaseController;
@Autowired
CardController cardController;
@PreAuthorize("hasPermission(#boardId, 'BOARD', 'ADMIN')")
@RequestMapping(value = "", method=RequestMethod.POST)
......@@ -90,13 +100,50 @@ public class ListController {
return list;
}
/**
* Get List.
* There are two sources for a list:
* - A List within the Board itself, used for small lists.
* - A List generated out of another Board.
*
* @param boardId
* @param listId
* @return
* @throws Exception
*/
@PreAuthorize("hasPermission(#boardId, 'BOARD', 'READ,WRITE,ADMIN')")
@RequestMapping(value = "/{listId}", method=RequestMethod.GET)
public @ResponseBody ListResource getList(
@PathVariable String boardId,
@PathVariable String listId) throws Exception {
return listCache.getItem(boardId, IdentifierTools.getIdFromName(listId));
String lid = IdentifierTools.getIdFromName(listId);
ListResource item = listCache.getItem(boardId, lid);
if( item.getListResourceType()==null || item.getListResourceType().equals(ListResourceType.INTERNAL)){
return item;
} else {
Map<String, String> cardList = cardController.getCardList(item.getBoard(), item.getPhase(), item.getFilter());
List<String> newList = new ArrayList<String>();
newList.addAll(cardList.keySet());
List<Card> cards = phaseController.getCards(item.getBoard(), item.getPhase(), item.getView(), newList);
Map<String,Option> optionList = new HashMap<String,Option>();
for( Card card : cards){
Option option = new Option();
option.setId(item.getBoard() + "-" + card.getId().toString());
option.setName( (String)card.getFields().get(item.getNameField()));
Map<String,String> newAttrs = new HashMap<String,String>();
Map<String, Object> fields = card.getFields();
for( Entry<String,Object> attr : fields.entrySet()){
newAttrs.put(attr.getKey(), attr.getValue().toString());
}
option.setAttributes(newAttrs);
optionList.put(option.getId(), option);
}
item.setItems(optionList);
return item;
}
}
public String getValue(
......
......@@ -62,9 +62,11 @@ public class TeamCache extends CacheImpl<Team> {
@Override
protected Map<String, String> getListFromStore(String... prefixs) throws Exception {
String pre = Integer.toString(prefixs.length) + " - ";
StringBuffer pre = new StringBuffer();
pre.append(Integer.toString(prefixs.length) + " - ");
for( int x=0; x < prefixs.length; x++){
pre = pre + prefixs[x] + ", ";
pre.append(prefixs[x]);
pre.append(", ");
}
logger.info("Prefixs: " + pre);
......
......@@ -95,7 +95,7 @@ public class TeamController {
return team;
}
@PreAuthorize("hasPermission(#teamId, 'TEAM', 'READ,WRITE,ADMIN')")
//@PreAuthorize("hasPermission(#teamId, 'TEAM', 'READ,WRITE,ADMIN')")
@RequestMapping(value = "/{teamId}", method=RequestMethod.GET)
public @ResponseBody Team getTeam(@PathVariable String teamId) throws Exception {
return teamCache.getItem(teamId);
......
......@@ -39,6 +39,7 @@ import org.apache.jackrabbit.ocm.manager.ObjectContentManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
......@@ -62,6 +63,7 @@ public class UserController {
@Resource(name="ocmFactory")
OcmMapperFactory ocmFactory;
@PreAuthorize("hasPermission('admin', 'TEAM', 'ADMIN')")
@RequestMapping(value = "", method=RequestMethod.POST)
public @ResponseBody User createUser(@RequestBody User user) throws Exception {
if( user.getPath()!=null ){
......@@ -115,6 +117,7 @@ public class UserController {
return taskList;
}
@PreAuthorize("hasPermission('admin', 'TEAM', 'ADMIN')")
@RequestMapping(value = "/{userId}", method=RequestMethod.DELETE)
public @ResponseBody void deleteUser(@PathVariable String userId) throws Exception {
Session session = ocmFactory.getOcm().getSession();
......
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
* (C) Copyright 2016 Peter Harrison
*
* This file is part of Gravity Workflow Automation.
*
......@@ -24,17 +25,78 @@ package nz.net.orcon.kanban.model;
import java.io.Serializable;
import java.util.Map;
import org.apache.jackrabbit.ocm.manager.enumconverter.EnumTypeConverter;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Collection;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Node;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Field;
@Node
public class ListResource extends AbstractNamedModelClass implements Serializable{
private static final long serialVersionUID = -1199910314402814094L;
@Field(converter=EnumTypeConverter.class)
private ListResourceType listResourceType;
@Field
private String board;
@Field
private String phase;
@Field
private String view;
@Field
private String filter;
@Field
private String nameField;
@Collection(jcrMandatory=false)
private Map<String, Option> items;
public ListResourceType getListResourceType() {
return listResourceType;
}
public void setListResourceType(ListResourceType listResourceType) {
this.listResourceType = listResourceType;
}
public String getBoard() {
return board;
}
public void setBoard(String board) {
this.board = board;
}
public String getPhase() {
return phase;
}
public void setPhase(String phase) {
this.phase = phase;
}
public String getView() {
return view;
}
public void setView(String view) {
this.view = view;
}
public String getFilter() {
return filter;
}
public void setFilter(String filter) {
this.filter = filter;
}
public void setItems(Map<String, Option> items) {
this.items = items;
}
......@@ -43,4 +105,12 @@ public class ListResource extends AbstractNamedModelClass implements Serializabl
return items;
}
public String getNameField() {
return nameField;
}
public void setNameField(String nameField) {
this.nameField = nameField;
}
}
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2016 Peter Harrison
*
* This file is part of Gravity Workflow Automation.
*
* Gravity Workflow Automation is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Gravity Workflow Automation is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gravity Workflow Automation.
* If not, see <http://www.gnu.org/licenses/>.
*/
package nz.net.orcon.kanban.model;
public enum ListResourceType {
INTERNAL,
BOARD;
}
......@@ -66,9 +66,8 @@ public class TemplateGroup extends AbstractNamedModelClass implements Serializab
sorted.put(entry.getValue().getIndex(), entry.getValue());
}
this.fields = new LinkedHashMap<String,TemplateField>();
for( Integer key: sorted.keySet()){
TemplateField field = sorted.get(key);
this.fields.put(field.getId(), field);
for( Entry<Integer,TemplateField> entry: sorted.entrySet()){
this.fields.put(entry.getValue().getId(), entry.getValue());
}
}
......
......@@ -174,16 +174,16 @@ public class CardConverter implements Converter{
}
card.setFields(map);
if("tasks".equalsIgnoreCase(reader.getNodeName())){
card.setTasks(new Long(reader.getValue()));
card.setTasks(Long.valueOf(reader.getValue()));
}
if("history".equalsIgnoreCase(reader.getNodeName())){
card.setHistory(new Long(reader.getValue()));
card.setHistory(Long.valueOf(reader.getValue()));
}
if("comments".equalsIgnoreCase(reader.getNodeName())){
card.setComments(new Long(reader.getValue()));
card.setComments(Long.valueOf(reader.getValue()));
}
if("alerts".equalsIgnoreCase(reader.getNodeName())){
card.setAlerts(new Long(reader.getValue()));
card.setAlerts(Long.valueOf(reader.getValue()));
}
return card;
}
......
......@@ -263,8 +263,6 @@ public class CardToolsImpl implements CardTools{
Map<Integer,String> phaseIndexMap = new TreeMap<Integer,String>();
Set<String> keySet = phases.keySet();
for( Entry<String,Phase> entry : phases.entrySet()){
phaseIndexMap.put(entry.getValue().getIndex(),entry.getKey());
}
......
......@@ -17,8 +17,7 @@ public class RepositoryIndexInitializer implements RepositoryInitializer{
ImmutableList fields =
ImmutableList.builder().add( "id","name" ).build();
NodeBuilder indexNodebuilder =
IndexUtils.createIndexDefinition(index, "gravityIndexes", true, false, fields, null);
IndexUtils.createIndexDefinition(index, "gravityIndexes", true, false, fields, null);
}
}
......@@ -13,8 +13,8 @@
</appender>
<!-- Application Loggers -->
<logger name="nz.net.orcon.gravity">
<level value="info" />
<logger name="nz.net.orcon.kanban">
<level value="debug" />
</logger>
<!-- 3rdparty Loggers -->
......
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:camel="http://camel.apache.org/schema/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
......@@ -14,7 +18,9 @@
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="variableInterpreter" class="nz.net.orcon.kanban.automation.VariableInterpreter"/>
......@@ -150,5 +156,10 @@
<!-- Cluster Management -->
<bean id="clusterManager" class="nz.net.orcon.kanban.automation.ClusterManagerImpl"/>
<camel:camelContext>
<!-- and then let Camel use those @Component scanned route builders -->
<camel:contextScan/>
</camel:camelContext>
</beans>
......@@ -128,6 +128,13 @@
top: 0;
}
.card-wrapper .card em span.card-phase {
float: right;
font-size: 10pt;
font-weight: normal;
position: relative;
}
.card-wrapper .card .card-modified {
display: block;
font-size: 9pt;
......
......@@ -31,7 +31,7 @@
<a href="" ng-click="setBoard(key,value)">{{value}}</a>
</li>
<li>
<a href="" ng-click="">New Board</a>
<a href="" ng-click="" data-toggle="modal" data-target="#newBoard">New Board</a>
</li>
</ul>
</li>
......@@ -79,6 +79,38 @@
</div>
</nav>
<!-- Modal -->
<div id="newBoard" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">New Board</h4>
</div>
<div class="modal-body">
<p>You are creating a new Board.</p>
</div>
<form class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-2" for="boardname">Name:</label>
<div class="col-xs-8">
<input type="text" class="form-control" id="boardname" placeholder="Board Name">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-xs-8">
<button type="submit" class="btn btn-default" data-dismiss="modal" ng-click="alertMe()">Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="container">
<!-- Main component for a primary marketing message or call to action -->
......@@ -113,8 +145,7 @@
</div>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<!-- <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> -->
<script src= "scripts/lib/angular.min.js"></script>
<script src="scripts/lib/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js"></script>
<script src="scripts/core/gravityApp.js"></script>
......
......@@ -116,30 +116,45 @@ var cards = {
drawCard: function(data, id, pos) {
var $card;
if (typeof template.templates[data.template] === "undefined")
tm = template.templates[data.template];
title = tm.name;
if(Object.keys(data.fields).length > 0){
title = title + ': ' + data.fields[Object.keys(data.fields)[0]];
}
if (typeof tm === "undefined")
return;
if (typeof pos == 'object') {
pos.append('<div class="card" data-id="' + id + '" data-phase="' + data.phase + '">' +
'<h1>' + (template.templates[data.template].name) + '<span class="card-id">#' + data.id + '</span></h1>' +
'<h1>' + title + '<span class="card-id">#' + data.id + '</span></h1>' +
'<em class="card-modified">' +
(data.modified && data.modified != null?"Modified ":"Created ") + convertDateToString((data.modified && data.modified != null)?data.modified: data.created) + ' by ' + (data.modified && data.modified != null?data.modifiedby:data.creator) + '' +
'<br />' +
'<strong>Phase: </strong>' + phase.phases[data.phase].name +
'</em>' +
(data.modified && data.modified != null?"Modified ":"Created ") +
convertDateToString((data.modified && data.modified != null)?data.modified: data.created) +
' by ' +
(data.modified && data.modified != null?data.modifiedby:data.creator) +
'' +
'<span class="card-phase">' +
phase.phases[data.phase].name +
'</span></em>' +
'</div>');
$card = pos.find('div.card').last();
} else {
var $cell = $('div.card').eq(pos);