TSK-743: Removed HATEOAS from classification- and workbasketdefinition exports
This commit is contained in:
parent
55c7453034
commit
835148c608
|
|
@ -355,7 +355,7 @@ include::../../../{snippets}/DeleteClassificationDocTest/http-request.adoc[]
|
||||||
|
|
||||||
include::../../../{snippets}/DeleteClassificationDocTest/http-response.adoc[]
|
include::../../../{snippets}/DeleteClassificationDocTest/http-response.adoc[]
|
||||||
|
|
||||||
== Classificationdefinition-Resource
|
== Classification-definition-Resource
|
||||||
|
|
||||||
[[classification-definitions]]
|
[[classification-definitions]]
|
||||||
=== Get all classification definitions
|
=== Get all classification definitions
|
||||||
|
|
@ -382,15 +382,15 @@ A `POST` request is used to import new classification-definitions.
|
||||||
|
|
||||||
This minimal example shows only the required fields to import a single new classification-definition. The <<classification, classification structure>> shows all possible fields for importing (and therefore creating) new classification-definitions.
|
This minimal example shows only the required fields to import a single new classification-definition. The <<classification, classification structure>> shows all possible fields for importing (and therefore creating) new classification-definitions.
|
||||||
|
|
||||||
include::../../../{snippets}/ImportClassificationdefinitions/http-request.adoc[]
|
include::../../../{snippets}/ImportClassificationDefinitions/http-request.adoc[]
|
||||||
|
|
||||||
==== Request Structure
|
==== Request Structure
|
||||||
|
|
||||||
include::../../../{snippets}/ImportClassificationdefinitions/request-fields.adoc[]
|
include::../../../{snippets}/ImportClassificationDefinitions/request-fields.adoc[]
|
||||||
|
|
||||||
==== Example Response
|
==== Example Response
|
||||||
|
|
||||||
include::../../../{snippets}/ImportClassificationdefinitions/http-response.adoc[]
|
include::../../../{snippets}/ImportClassificationDefinitions/http-response.adoc[]
|
||||||
|
|
||||||
== Workbaskets-Resource
|
== Workbaskets-Resource
|
||||||
|
|
||||||
|
|
@ -540,7 +540,7 @@ include::../../../{snippets}/CreateWorkbasketDocTest/http-response.adoc[]
|
||||||
The response-body is essentially the same as for getting a single classification. +
|
The response-body is essentially the same as for getting a single classification. +
|
||||||
Therefore for the response fields you can refer to the structure of the <<classification, single classification>>.
|
Therefore for the response fields you can refer to the structure of the <<classification, single classification>>.
|
||||||
|
|
||||||
== Workbasketdefinition-Resource
|
== Workbasket-definition-resource
|
||||||
|
|
||||||
=== Get all workbasket definitions
|
=== Get all workbasket definitions
|
||||||
|
|
||||||
|
|
@ -548,16 +548,16 @@ A `GET` request is used to get all workbasket definitions.
|
||||||
|
|
||||||
==== Example Request
|
==== Example Request
|
||||||
|
|
||||||
include::../../../{snippets}/ExportWorkbasktdefinitionsDocTest/http-request.adoc[]
|
include::../../../{snippets}/ExportWorkbasketdefinitionsDocTest/http-request.adoc[]
|
||||||
|
|
||||||
==== Example Response
|
==== Example Response
|
||||||
|
|
||||||
include::../../../{snippets}/ExportWorkbasktdefinitionsDocTest/http-response.adoc[]
|
include::../../../{snippets}/ExportWorkbasketdefinitionsDocTest/http-response.adoc[]
|
||||||
|
|
||||||
[[workbasket-definitions]]
|
[[workbasket-definitions]]
|
||||||
==== Response Structure
|
==== Response Structure
|
||||||
|
|
||||||
include::../../../{snippets}/ExportWorkbasktdefinitionsDocTest/response-fields.adoc[]
|
include::../../../{snippets}/ExportWorkbasketdefinitionsDocTest/response-fields.adoc[]
|
||||||
|
|
||||||
=== Import new workbasket-definitions
|
=== Import new workbasket-definitions
|
||||||
|
|
||||||
|
|
@ -567,15 +567,15 @@ A `POST` request is used to import new workbasket-definitions.
|
||||||
|
|
||||||
This minimal example shows only the required fields to import a single new workbasket-definition. The <<workbasket, workbasket structure>> shows all possible fields for importing (and therefore creating) new workbasket-definitions.
|
This minimal example shows only the required fields to import a single new workbasket-definition. The <<workbasket, workbasket structure>> shows all possible fields for importing (and therefore creating) new workbasket-definitions.
|
||||||
|
|
||||||
include::../../../{snippets}/ImportWorkbasketdefinitions/http-request.adoc[]
|
include::../../../{snippets}/ImportWorkbasketDefinitions/http-request.adoc[]
|
||||||
|
|
||||||
==== Request Structure
|
==== Request Structure
|
||||||
|
|
||||||
include::../../../{snippets}/ImportWorkbasketdefinitions/request-fields.adoc[]
|
include::../../../{snippets}/ImportWorkbasketDefinitions/request-fields.adoc[]
|
||||||
|
|
||||||
==== Response Structure
|
==== Response Structure
|
||||||
|
|
||||||
include::../../../{snippets}/ImportWorkbasketdefinitions/http-response.adoc[]
|
include::../../../{snippets}/ImportWorkbasketDefinitions/http-response.adoc[]
|
||||||
|
|
||||||
== WorkbasketAccessItems-Resource
|
== WorkbasketAccessItems-Resource
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.pr
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.partWithName;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.requestParts;
|
||||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
|
@ -33,59 +36,61 @@ import pro.taskana.rest.RestConfiguration;
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@SpringBootTest(classes = RestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(classes = RestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
public class ClassificationDefinitionControllerRestDocumentation {
|
public class ClassificationDefinitionControllerRestDocumentation {
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
int port;
|
int port;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
|
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WebApplicationContext context;
|
private WebApplicationContext context;
|
||||||
|
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
private FieldDescriptor[] classificationdefinitionsFieldDescriptors;
|
private FieldDescriptor[] classificationDefinitionsFieldDescriptors;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
document("{methodName}",
|
document("{methodName}",
|
||||||
preprocessRequest(prettyPrint()),
|
preprocessRequest(prettyPrint()),
|
||||||
preprocessResponse(prettyPrint()));
|
preprocessResponse(prettyPrint()));
|
||||||
|
|
||||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||||
.apply(springSecurity())
|
.apply(springSecurity())
|
||||||
.apply(documentationConfiguration(this.restDocumentation)
|
.apply(documentationConfiguration(this.restDocumentation)
|
||||||
.operationPreprocessors()
|
.operationPreprocessors()
|
||||||
.withResponseDefaults(prettyPrint())
|
.withResponseDefaults(prettyPrint())
|
||||||
.withRequestDefaults(prettyPrint()))
|
.withRequestDefaults(prettyPrint()))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
classificationdefinitionsFieldDescriptors = new FieldDescriptor[] {
|
classificationDefinitionsFieldDescriptors = new FieldDescriptor[] {
|
||||||
subsectionWithPath("[]").description("An array of <<classification, classifications>>")
|
subsectionWithPath("[]").description("An array of <<ClassificationResource, classifications>>")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void exportAllClassificationdefinitions() throws Exception {
|
public void exportAllClassificationDefinitions() throws Exception {
|
||||||
this.mockMvc.perform(RestDocumentationRequestBuilders
|
this.mockMvc.perform(RestDocumentationRequestBuilders
|
||||||
.get("http://127.0.0.1:" + port + "/v1/classificationdefinitions")
|
.get("http://127.0.0.1:" + port + "/v1/classification-definitions")
|
||||||
.accept("application/json")
|
.accept("application/json")
|
||||||
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"))
|
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"))
|
||||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||||
.andDo(MockMvcRestDocumentation.document("ExportClassificationdefinitionsDocTest",
|
.andDo(MockMvcRestDocumentation.document("ExportClassificationDefinitionsDocTest",
|
||||||
responseFields(classificationdefinitionsFieldDescriptors)));
|
responseFields(classificationDefinitionsFieldDescriptors)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void importClassificationdefinitions() throws Exception {
|
public void importClassificationDefinitions() throws Exception {
|
||||||
String definitionString = "[{\"key\":\"Key0815\", \"domain\":\"DOMAIN_B\"}]";
|
String definitionString = "[{\"key\":\"Key0815\", \"domain\":\"DOMAIN_B\"}]";
|
||||||
this.mockMvc.perform(RestDocumentationRequestBuilders
|
|
||||||
.post("http://127.0.0.1:" + port + "/v1/classificationdefinitions/")
|
this.mockMvc.perform(multipart("http://127.0.0.1:" + port + "/v1/classification-definitions")
|
||||||
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x")
|
.file("file",
|
||||||
.contentType("application/json")
|
definitionString.getBytes())
|
||||||
.content(definitionString))
|
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"))
|
||||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||||
.andDo(MockMvcRestDocumentation.document("ImportClassificationdefinitions",
|
.andDo(document("ImportClassificationDefinitions", requestParts(
|
||||||
requestFields(subsectionWithPath("[]").description("An array of <<classification-definitions, classifications>>"))));
|
partWithName("file").description("The file to upload"))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.pr
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.partWithName;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.requestParts;
|
||||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
|
@ -33,65 +36,67 @@ import pro.taskana.rest.RestConfiguration;
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@SpringBootTest(classes = RestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(classes = RestConfiguration.class, webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
public class WorkbasketDefinitionControllerRestDocumentation {
|
public class WorkbasketDefinitionControllerRestDocumentation {
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
int port;
|
int port;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
|
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WebApplicationContext context;
|
private WebApplicationContext context;
|
||||||
|
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
private FieldDescriptor[] workbasketdefinitionsFieldDescriptors;
|
private FieldDescriptor[] workbasketDefinitionsFieldDescriptors;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
document("{methodName}",
|
document("{methodName}",
|
||||||
preprocessRequest(prettyPrint()),
|
preprocessRequest(prettyPrint()),
|
||||||
preprocessResponse(prettyPrint()));
|
preprocessResponse(prettyPrint()));
|
||||||
|
|
||||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
|
||||||
.apply(springSecurity())
|
|
||||||
.apply(documentationConfiguration(this.restDocumentation)
|
|
||||||
.operationPreprocessors()
|
|
||||||
.withResponseDefaults(prettyPrint())
|
|
||||||
.withRequestDefaults(prettyPrint()))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||||
workbasketdefinitionsFieldDescriptors = new FieldDescriptor[] {
|
.apply(springSecurity())
|
||||||
subsectionWithPath("[]").description("An array of <<workbasket, workbaskets>>")
|
.apply(documentationConfiguration(this.restDocumentation)
|
||||||
|
.operationPreprocessors()
|
||||||
|
.withResponseDefaults(prettyPrint())
|
||||||
|
.withRequestDefaults(prettyPrint()))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
workbasketDefinitionsFieldDescriptors = new FieldDescriptor[] {
|
||||||
|
subsectionWithPath("[]").description("An array of <<WorkbasketDefinitions, workbasketsDefinitions>>")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void exportAllWorkbasketDefinitions() throws Exception {
|
public void exportAllWorkbasketDefinitions() throws Exception {
|
||||||
this.mockMvc.perform(RestDocumentationRequestBuilders
|
this.mockMvc.perform(RestDocumentationRequestBuilders
|
||||||
.get("http://127.0.0.1:" + port + "/v1/workbasketdefinitions")
|
.get("http://127.0.0.1:" + port + "/v1/workbasket-definitions")
|
||||||
.accept("application/json")
|
.accept("application/json")
|
||||||
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"))
|
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"))
|
||||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||||
.andDo(MockMvcRestDocumentation.document("ExportWorkbasktdefinitionsDocTest",
|
.andDo(MockMvcRestDocumentation.document("ExportWorkbasketdefinitionsDocTest",
|
||||||
responseFields(workbasketdefinitionsFieldDescriptors)));
|
responseFields(workbasketDefinitionsFieldDescriptors)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void importWorkbasketdefinition() throws Exception {
|
public void importWorkbasketDefinition() throws Exception {
|
||||||
this.mockMvc.perform(RestDocumentationRequestBuilders
|
String definitionString = "["
|
||||||
.post("http://127.0.0.1:" + port + "/v1/workbasketdefinitions")
|
+ "{"
|
||||||
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x")
|
+ "\"distributionTargets\":[], "
|
||||||
.contentType("application/json")
|
+ "\"authorizations\":[], "
|
||||||
.content("["
|
+ "\"workbasket\": {\"name\":\"wbblabla\", \"key\":\"neuerKeyXy\", \"domain\": \"DOMAIN_A\", \"type\":\"GROUP\" , \"workbasketId\":\"gibtsNed\"}"
|
||||||
+ "{"
|
+ "}"
|
||||||
+ "\"distributionTargets\":[], "
|
+ "]";
|
||||||
+ "\"authorizations\":[], "
|
|
||||||
+ "\"workbasket\": {\"name\":\"wbblabla\", \"key\":\"neuerKeyXy\", \"domain\": \"DOMAIN_A\", \"type\":\"GROUP\"}"
|
this.mockMvc.perform(multipart("http://127.0.0.1:" + port + "/v1/workbasket-definitions")
|
||||||
+ "}"
|
.file("file",
|
||||||
+ "]"))
|
definitionString.getBytes())
|
||||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
.header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"))
|
||||||
.andDo(MockMvcRestDocumentation.document("ImportWorkbasketdefinitions",
|
.andExpect(MockMvcResultMatchers.status().isOk())
|
||||||
requestFields(subsectionWithPath("[]").description("An array of <<workbasket, workbaskets>>"))));
|
.andDo(document("ImportWorkbasketDefinitions", requestParts(
|
||||||
|
partWithName("file").description("The file to upload"))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package pro.taskana.rest;
|
package pro.taskana.rest;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
@ -9,13 +10,12 @@ import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import pro.taskana.Classification;
|
import pro.taskana.Classification;
|
||||||
import pro.taskana.ClassificationQuery;
|
import pro.taskana.ClassificationQuery;
|
||||||
|
|
@ -27,54 +27,72 @@ import pro.taskana.exceptions.ConcurrencyException;
|
||||||
import pro.taskana.exceptions.DomainNotFoundException;
|
import pro.taskana.exceptions.DomainNotFoundException;
|
||||||
import pro.taskana.exceptions.InvalidArgumentException;
|
import pro.taskana.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.exceptions.NotAuthorizedException;
|
import pro.taskana.exceptions.NotAuthorizedException;
|
||||||
|
import pro.taskana.rest.resource.ClassificationResource;
|
||||||
|
import pro.taskana.rest.resource.ClassificationResourceAssembler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for Importing / Exporting classifications.
|
* Controller for Importing / Exporting classifications.
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(path = "/v1/classificationdefinitions", produces = {MediaType.APPLICATION_JSON_VALUE})
|
@RequestMapping(path = "/v1/classification-definitions", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||||
public class ClassificationDefinitionController {
|
public class ClassificationDefinitionController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ClassificationService classificationService;
|
private ClassificationService classificationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ClassificationResourceAssembler classificationResourceAssembler;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@Transactional(readOnly = true, rollbackFor = Exception.class)
|
@Transactional(readOnly = true, rollbackFor = Exception.class)
|
||||||
public ResponseEntity<List<ClassificationSummary>> exportClassifications(
|
public ResponseEntity<List<ClassificationResource>> exportClassifications(
|
||||||
@RequestParam(required = false) String domain) {
|
@RequestParam(required = false) String domain)
|
||||||
|
throws ClassificationNotFoundException, DomainNotFoundException, ConcurrencyException, InvalidArgumentException,
|
||||||
|
NotAuthorizedException, ClassificationAlreadyExistException {
|
||||||
ClassificationQuery query = classificationService.createClassificationQuery();
|
ClassificationQuery query = classificationService.createClassificationQuery();
|
||||||
List<ClassificationSummary> summaries = domain != null ? query.domainIn(domain).list() : query.list();
|
|
||||||
|
|
||||||
return new ResponseEntity<>(summaries, HttpStatus.OK);
|
List<ClassificationSummary> summaries = domain != null ? query.domainIn(domain).list() : query.list();
|
||||||
|
List<ClassificationResource> export = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ClassificationSummary summary : summaries) {
|
||||||
|
Classification classification = classificationService.getClassification(summary.getKey(),
|
||||||
|
summary.getDomain());
|
||||||
|
|
||||||
|
export.add(classificationResourceAssembler.toDefinition(classification));
|
||||||
|
}
|
||||||
|
return new ResponseEntity<>(export, HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public ResponseEntity<String> importClassifications(
|
public ResponseEntity<String> importClassifications(
|
||||||
@RequestBody List<Classification> classifications) throws InvalidArgumentException {
|
@RequestParam("file") MultipartFile file)
|
||||||
|
throws InvalidArgumentException, NotAuthorizedException, ConcurrencyException, ClassificationNotFoundException,
|
||||||
|
ClassificationAlreadyExistException, DomainNotFoundException, IOException {
|
||||||
Map<String, String> systemIds = classificationService.createClassificationQuery()
|
Map<String, String> systemIds = classificationService.createClassificationQuery()
|
||||||
.list()
|
.list()
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(i -> i.getKey() + "|" + i.getDomain(), ClassificationSummary::getId));
|
.collect(Collectors.toMap(i -> i.getKey() + "|" + i.getDomain(), ClassificationSummary::getId));
|
||||||
try {
|
|
||||||
for (Classification classification : classifications) {
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
if (systemIds.containsKey(classification.getKey() + "|" + classification.getDomain())) {
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
classificationService.updateClassification(classification);
|
List<ClassificationResource> classificationsDefinitions = mapper.readValue(file.getInputStream(),
|
||||||
} else {
|
new TypeReference<List<ClassificationResource>>() {
|
||||||
classificationService.createClassification(classification);
|
|
||||||
}
|
});
|
||||||
|
|
||||||
|
for (ClassificationResource classification : classificationsDefinitions) {
|
||||||
|
if (systemIds.containsKey(classification.getKey() + "|" + classification.getDomain())) {
|
||||||
|
classificationService.updateClassification(classificationResourceAssembler.toModel(classification));
|
||||||
|
} else {
|
||||||
|
classificationService.createClassification(classificationResourceAssembler.toModel(classification));
|
||||||
}
|
}
|
||||||
} catch (NotAuthorizedException e) {
|
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
} catch (ClassificationNotFoundException | DomainNotFoundException e) {
|
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
|
||||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
|
||||||
} catch (ClassificationAlreadyExistException e) {
|
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
|
||||||
return new ResponseEntity<>(HttpStatus.CONFLICT);
|
|
||||||
// TODO why is this occuring???
|
|
||||||
} catch (ConcurrencyException e) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
return new ResponseEntity<>(HttpStatus.OK);
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,12 @@ import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import pro.taskana.Workbasket;
|
import pro.taskana.Workbasket;
|
||||||
import pro.taskana.WorkbasketAccessItem;
|
import pro.taskana.WorkbasketAccessItem;
|
||||||
|
|
@ -31,25 +30,43 @@ import pro.taskana.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.exceptions.WorkbasketAlreadyExistException;
|
import pro.taskana.exceptions.WorkbasketAlreadyExistException;
|
||||||
import pro.taskana.exceptions.WorkbasketNotFoundException;
|
import pro.taskana.exceptions.WorkbasketNotFoundException;
|
||||||
import pro.taskana.rest.resource.WorkbasketDefinition;
|
import pro.taskana.rest.resource.WorkbasketDefinition;
|
||||||
|
import pro.taskana.rest.resource.WorkbasketDefinitionAssembler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for all {@link WorkbasketDefinition} related endpoints.
|
* Controller for all {@link WorkbasketDefinition} related endpoints.
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(path = "/v1/workbasketdefinitions", produces = {MediaType.APPLICATION_JSON_VALUE})
|
@RequestMapping(path = "/v1/workbasket-definitions", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||||
public class WorkbasketDefinitionController {
|
public class WorkbasketDefinitionController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private WorkbasketService workbasketService;
|
private WorkbasketService workbasketService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WorkbasketDefinitionAssembler workbasketDefinitionAssembler;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@Transactional(readOnly = true, rollbackFor = Exception.class)
|
@Transactional(readOnly = true, rollbackFor = Exception.class)
|
||||||
public ResponseEntity<List<WorkbasketSummary>> exportWorkbaskets(@RequestParam(required = false) String domain) {
|
public ResponseEntity<List<WorkbasketDefinition>> exportWorkbaskets(@RequestParam(required = false) String domain)
|
||||||
|
throws NotAuthorizedException, WorkbasketNotFoundException {
|
||||||
|
|
||||||
WorkbasketQuery workbasketQuery = workbasketService.createWorkbasketQuery();
|
WorkbasketQuery workbasketQuery = workbasketService.createWorkbasketQuery();
|
||||||
List<WorkbasketSummary> workbasketSummaryList = domain != null
|
List<WorkbasketSummary> workbasketSummaryList = domain != null
|
||||||
? workbasketQuery.domainIn(domain).list()
|
? workbasketQuery.domainIn(domain).list()
|
||||||
: workbasketQuery.list();
|
: workbasketQuery.list();
|
||||||
return new ResponseEntity<>(workbasketSummaryList, HttpStatus.OK);
|
List<WorkbasketDefinition> basketExports = new ArrayList<>();
|
||||||
|
for (WorkbasketSummary summary : workbasketSummaryList) {
|
||||||
|
Workbasket workbasket = workbasketService.getWorkbasket(summary.getId());
|
||||||
|
basketExports.add(workbasketDefinitionAssembler.toDefinition(workbasket));
|
||||||
|
}
|
||||||
|
return new ResponseEntity<>(basketExports, HttpStatus.OK);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -57,85 +74,76 @@ public class WorkbasketDefinitionController {
|
||||||
* we want to have an option to import all settings at once. When a logical equal (key and domain are equal)
|
* we want to have an option to import all settings at once. When a logical equal (key and domain are equal)
|
||||||
* workbasket already exists an update will be executed. Otherwise a new workbasket will be created.
|
* workbasket already exists an update will be executed. Otherwise a new workbasket will be created.
|
||||||
*
|
*
|
||||||
* @param definitions the list of workbasket definitions which will be imported to the current system.
|
* @param file the list of workbasket definitions which will be imported to the current system.
|
||||||
* @return Return answer is determined by the status code: 200 - all good 400 - list state error (referring to non
|
* @return Return answer is determined by the status code: 200 - all good 400 - list state error (referring to non
|
||||||
* existing id's) 401 - not authorized
|
* existing id's) 401 - not authorized
|
||||||
*/
|
*/
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public ResponseEntity<String> importWorkbaskets(@RequestBody List<WorkbasketDefinition> definitions) {
|
public ResponseEntity<String> importWorkbaskets(@RequestParam("file") MultipartFile file)
|
||||||
try {
|
throws IOException, NotAuthorizedException, DomainNotFoundException, InvalidWorkbasketException,
|
||||||
// key: logical ID
|
WorkbasketAlreadyExistException, WorkbasketNotFoundException, InvalidArgumentException {
|
||||||
// value: system ID (in database)
|
|
||||||
Map<String, String> systemIds = workbasketService.createWorkbasketQuery()
|
|
||||||
.list()
|
|
||||||
.stream()
|
|
||||||
.collect(Collectors.toMap(this::logicalId, WorkbasketSummary::getId));
|
|
||||||
|
|
||||||
// key: old system ID
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
// value: system ID
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
Map<String, String> idConversion = new HashMap<>();
|
List<WorkbasketDefinition> definitions = mapper.readValue(file.getInputStream(),
|
||||||
|
new TypeReference<List<WorkbasketDefinition>>() {
|
||||||
|
|
||||||
// STEP 1: update or create workbaskets from the import
|
});
|
||||||
for (WorkbasketDefinition definition : definitions) {
|
|
||||||
Workbasket importedWb = definition.workbasket;
|
|
||||||
Workbasket workbasket;
|
|
||||||
if (systemIds.containsKey(logicalId(importedWb))) {
|
|
||||||
workbasket = workbasketService.updateWorkbasket(importedWb);
|
|
||||||
} else {
|
|
||||||
workbasket = workbasketService.createWorkbasket(importedWb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since we would have a n² runtime when doing a lookup and updating the access items we decided to
|
// key: logical ID
|
||||||
// simply delete all existing accessItems and create new ones.
|
// value: system ID (in database)
|
||||||
for (WorkbasketAccessItem accessItem : workbasketService.getWorkbasketAccessItems(workbasket.getId())) {
|
Map<String, String> systemIds = workbasketService.createWorkbasketQuery()
|
||||||
workbasketService.deleteWorkbasketAccessItem(accessItem.getId());
|
.list()
|
||||||
}
|
.stream()
|
||||||
for (WorkbasketAccessItem authorization : definition.authorizations) {
|
.collect(Collectors.toMap(this::logicalId, WorkbasketSummary::getId));
|
||||||
workbasketService.createWorkbasketAccessItem(authorization);
|
|
||||||
}
|
// key: old system ID
|
||||||
idConversion.put(definition.workbasket.getId(), workbasket.getId());
|
// value: system ID
|
||||||
|
Map<String, String> idConversion = new HashMap<>();
|
||||||
|
|
||||||
|
// STEP 1: update or create workbaskets from the import
|
||||||
|
for (WorkbasketDefinition definition : definitions) {
|
||||||
|
Workbasket importedWb = workbasketDefinitionAssembler.toModel(definition.workbasket);
|
||||||
|
Workbasket workbasket;
|
||||||
|
if (systemIds.containsKey(logicalId(importedWb))) {
|
||||||
|
workbasket = workbasketService.updateWorkbasket(importedWb);
|
||||||
|
} else {
|
||||||
|
workbasket = workbasketService.createWorkbasket(importedWb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// STEP 2: update distribution targets
|
// Since we would have a n² runtime when doing a lookup and updating the access items we decided to
|
||||||
// This can not be done in step 1 because the system IDs are only known after step 1
|
// simply delete all existing accessItems and create new ones.
|
||||||
for (WorkbasketDefinition definition : definitions) {
|
for (WorkbasketAccessItem accessItem : workbasketService.getWorkbasketAccessItems(workbasket.getId())) {
|
||||||
List<String> distributionTargets = new ArrayList<>();
|
workbasketService.deleteWorkbasketAccessItem(accessItem.getId());
|
||||||
for (String oldId : definition.distributionTargets) {
|
|
||||||
if (idConversion.containsKey(oldId)) {
|
|
||||||
distributionTargets.add(idConversion.get(oldId));
|
|
||||||
} else {
|
|
||||||
throw new InvalidWorkbasketException(
|
|
||||||
String.format(
|
|
||||||
"invalid import state: Workbasket '%s' does not exist in the given import list",
|
|
||||||
oldId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
workbasketService.setDistributionTargets(
|
|
||||||
// no verification necessary since the workbasket was already imported in step 1.
|
|
||||||
idConversion.get(definition.workbasket.getId()), distributionTargets);
|
|
||||||
}
|
}
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
for (WorkbasketAccessItem authorization : definition.authorizations) {
|
||||||
} catch (WorkbasketNotFoundException e) {
|
workbasketService.createWorkbasketAccessItem(authorization);
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
}
|
||||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
idConversion.put(importedWb.getId(), workbasket.getId());
|
||||||
} catch (InvalidWorkbasketException e) {
|
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
} catch (NotAuthorizedException e) {
|
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
} catch (InvalidArgumentException e) {
|
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
|
||||||
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
|
|
||||||
} catch (WorkbasketAlreadyExistException e) {
|
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
|
||||||
return new ResponseEntity<>(HttpStatus.CONFLICT);
|
|
||||||
} catch (DomainNotFoundException e) {
|
|
||||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// STEP 2: update distribution targets
|
||||||
|
// This can not be done in step 1 because the system IDs are only known after step 1
|
||||||
|
for (WorkbasketDefinition definition : definitions) {
|
||||||
|
List<String> distributionTargets = new ArrayList<>();
|
||||||
|
for (String oldId : definition.distributionTargets) {
|
||||||
|
if (idConversion.containsKey(oldId)) {
|
||||||
|
distributionTargets.add(idConversion.get(oldId));
|
||||||
|
} else {
|
||||||
|
throw new InvalidWorkbasketException(
|
||||||
|
String.format(
|
||||||
|
"invalid import state: Workbasket '%s' does not exist in the given import list",
|
||||||
|
oldId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workbasketService.setDistributionTargets(
|
||||||
|
// no verification necessary since the workbasket was already imported in step 1.
|
||||||
|
idConversion.get(definition.workbasket.getWorkbasketId()), distributionTargets);
|
||||||
|
}
|
||||||
|
return new ResponseEntity<>(HttpStatus.OK);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String logicalId(WorkbasketSummary workbasket) {
|
private String logicalId(WorkbasketSummary workbasket) {
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,18 @@ public class ClassificationResourceAssembler {
|
||||||
@Autowired
|
@Autowired
|
||||||
ClassificationService classificationService;
|
ClassificationService classificationService;
|
||||||
|
|
||||||
public ClassificationResource toResource(Classification classification) throws ClassificationNotFoundException,
|
public ClassificationResource toResource(Classification classification)
|
||||||
NotAuthorizedException, ClassificationAlreadyExistException, ConcurrencyException, DomainNotFoundException,
|
throws InvalidArgumentException, ConcurrencyException, ClassificationNotFoundException, DomainNotFoundException,
|
||||||
InvalidArgumentException {
|
ClassificationAlreadyExistException, NotAuthorizedException {
|
||||||
ClassificationResource resource = new ClassificationResource();
|
return this.createResource(classification);
|
||||||
BeanUtils.copyProperties(classification, resource);
|
}
|
||||||
// need to be set by hand, because they are named different, or have different types
|
|
||||||
resource.setClassificationId(classification.getId());
|
public ClassificationResource toDefinition(Classification classification)
|
||||||
resource.setCreated(classification.getCreated().toString());
|
throws InvalidArgumentException, ConcurrencyException, ClassificationNotFoundException, DomainNotFoundException,
|
||||||
resource.setModified(classification.getModified().toString());
|
ClassificationAlreadyExistException, NotAuthorizedException {
|
||||||
return addLinks(resource, classification);
|
ClassificationResource resource = this.createResource(classification);
|
||||||
|
resource.removeLinks();
|
||||||
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Classification toModel(ClassificationResource classificationResource) {
|
public Classification toModel(ClassificationResource classificationResource) {
|
||||||
|
|
@ -56,6 +58,22 @@ public class ClassificationResourceAssembler {
|
||||||
return classification;
|
return classification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClassificationResource createResource(Classification classification)
|
||||||
|
throws NotAuthorizedException, ConcurrencyException, InvalidArgumentException, DomainNotFoundException,
|
||||||
|
ClassificationAlreadyExistException, ClassificationNotFoundException {
|
||||||
|
ClassificationResource resource = new ClassificationResource();
|
||||||
|
BeanUtils.copyProperties(classification, resource);
|
||||||
|
// need to be set by hand, because they are named different, or have different types
|
||||||
|
resource.setClassificationId(classification.getId());
|
||||||
|
if(classification.getCreated() != null){
|
||||||
|
resource.setCreated(classification.getCreated().toString());
|
||||||
|
}
|
||||||
|
if(classification.getModified() != null){
|
||||||
|
resource.setModified(classification.getModified().toString());
|
||||||
|
}
|
||||||
|
return addLinks(resource, classification);
|
||||||
|
}
|
||||||
|
|
||||||
private ClassificationResource addLinks(ClassificationResource resource, Classification classification)
|
private ClassificationResource addLinks(ClassificationResource resource, Classification classification)
|
||||||
throws ClassificationNotFoundException, NotAuthorizedException, ClassificationAlreadyExistException,
|
throws ClassificationNotFoundException, NotAuthorizedException, ClassificationAlreadyExistException,
|
||||||
ConcurrencyException, DomainNotFoundException, InvalidArgumentException {
|
ConcurrencyException, DomainNotFoundException, InvalidArgumentException {
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,13 @@ public class WorkbasketDefinition {
|
||||||
|
|
||||||
public Set<String> distributionTargets;
|
public Set<String> distributionTargets;
|
||||||
public List<WorkbasketAccessItem> authorizations;
|
public List<WorkbasketAccessItem> authorizations;
|
||||||
public Workbasket workbasket;
|
public WorkbasketResource workbasket;
|
||||||
|
|
||||||
public WorkbasketDefinition() {
|
public WorkbasketDefinition() {
|
||||||
// necessary for de-serializing
|
// necessary for de-serializing
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorkbasketDefinition(Workbasket workbasket,
|
public WorkbasketDefinition(WorkbasketResource workbasket,
|
||||||
Set<String> distributionTargets,
|
Set<String> distributionTargets,
|
||||||
List<WorkbasketAccessItem> authorizations) {
|
List<WorkbasketAccessItem> authorizations) {
|
||||||
super();
|
super();
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,28 @@
|
||||||
package pro.taskana.rest.resource;
|
package pro.taskana.rest.resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import pro.taskana.Workbasket;
|
import pro.taskana.Workbasket;
|
||||||
import pro.taskana.WorkbasketAccessItem;
|
import pro.taskana.WorkbasketAccessItem;
|
||||||
import pro.taskana.WorkbasketService;
|
import pro.taskana.WorkbasketService;
|
||||||
import pro.taskana.WorkbasketSummary;
|
import pro.taskana.WorkbasketSummary;
|
||||||
|
import pro.taskana.exceptions.DomainNotFoundException;
|
||||||
|
import pro.taskana.exceptions.InvalidArgumentException;
|
||||||
|
import pro.taskana.exceptions.InvalidWorkbasketException;
|
||||||
import pro.taskana.exceptions.NotAuthorizedException;
|
import pro.taskana.exceptions.NotAuthorizedException;
|
||||||
|
import pro.taskana.exceptions.WorkbasketAlreadyExistException;
|
||||||
import pro.taskana.exceptions.WorkbasketNotFoundException;
|
import pro.taskana.exceptions.WorkbasketNotFoundException;
|
||||||
|
import pro.taskana.impl.WorkbasketImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms {@link Workbasket} into a {@link WorkbasketDefinition}
|
* Transforms {@link Workbasket} into a {@link WorkbasketDefinition}
|
||||||
|
|
@ -28,7 +37,7 @@ public class WorkbasketDefinitionAssembler {
|
||||||
/**
|
/**
|
||||||
* maps the distro targets to their id to remove overhead.
|
* maps the distro targets to their id to remove overhead.
|
||||||
*
|
*
|
||||||
* @param basket
|
* @param workbasket
|
||||||
* {@link Workbasket} which will be converted
|
* {@link Workbasket} which will be converted
|
||||||
* @return a {@link WorkbasketDefinition}, containing the {@code basket}, its distribution targets and its
|
* @return a {@link WorkbasketDefinition}, containing the {@code basket}, its distribution targets and its
|
||||||
* authorizations
|
* authorizations
|
||||||
|
|
@ -37,16 +46,37 @@ public class WorkbasketDefinitionAssembler {
|
||||||
* @throws WorkbasketNotFoundException
|
* @throws WorkbasketNotFoundException
|
||||||
* if {@code basket} is an unknown workbasket
|
* if {@code basket} is an unknown workbasket
|
||||||
*/
|
*/
|
||||||
public WorkbasketDefinition toDefinition(Workbasket basket)
|
public WorkbasketDefinition toDefinition(Workbasket workbasket)
|
||||||
throws NotAuthorizedException, WorkbasketNotFoundException {
|
throws NotAuthorizedException, WorkbasketNotFoundException {
|
||||||
|
|
||||||
|
WorkbasketResource basket = new WorkbasketResource();
|
||||||
|
BeanUtils.copyProperties(workbasket, basket);
|
||||||
|
basket.setWorkbasketId(workbasket.getId());
|
||||||
|
basket.setModified(workbasket.getModified().toString());
|
||||||
|
basket.setCreated(workbasket.getCreated().toString());
|
||||||
|
|
||||||
List<WorkbasketAccessItem> authorizations = new ArrayList<>();
|
List<WorkbasketAccessItem> authorizations = new ArrayList<>();
|
||||||
for (WorkbasketAccessItem accessItem : workbasketService.getWorkbasketAccessItems(basket.getKey())) {
|
for (WorkbasketAccessItem accessItem : workbasketService.getWorkbasketAccessItems(basket.getKey())) {
|
||||||
authorizations.add(accessItem);
|
authorizations.add(accessItem);
|
||||||
}
|
}
|
||||||
Set<String> distroTargets = workbasketService.getDistributionTargets(basket.getId())
|
Set<String> distroTargets = workbasketService.getDistributionTargets(workbasket.getId())
|
||||||
.stream()
|
.stream()
|
||||||
.map(WorkbasketSummary::getId)
|
.map(WorkbasketSummary::getId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
return new WorkbasketDefinition(basket, distroTargets, authorizations);
|
return new WorkbasketDefinition(basket, distroTargets, authorizations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Workbasket toModel(WorkbasketResource wbResource) {
|
||||||
|
WorkbasketImpl workbasket = (WorkbasketImpl) workbasketService.newWorkbasket(wbResource.key, wbResource.domain);
|
||||||
|
BeanUtils.copyProperties(wbResource, workbasket);
|
||||||
|
|
||||||
|
workbasket.setId(wbResource.workbasketId);
|
||||||
|
if (wbResource.getModified() != null) {
|
||||||
|
workbasket.setModified(Instant.parse(wbResource.modified));
|
||||||
|
}
|
||||||
|
if (wbResource.getCreated() != null) {
|
||||||
|
workbasket.setCreated(Instant.parse(wbResource.created));
|
||||||
|
}
|
||||||
|
return workbasket;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import {WorkbasketDefinitionService} from './services/workbasket-definition/work
|
||||||
import {ClassificationsService} from '../services/classifications/classifications.service';
|
import {ClassificationsService} from '../services/classifications/classifications.service';
|
||||||
import {ClassificationCategoriesService} from 'app/services/classifications/classification-categories.service';
|
import {ClassificationCategoriesService} from 'app/services/classifications/classification-categories.service';
|
||||||
import { AccessItemsManagementComponent } from 'app/administration/access-items-management/access-items-management.component';
|
import { AccessItemsManagementComponent } from 'app/administration/access-items-management/access-items-management.component';
|
||||||
|
import { ImportExportService } from './services/import-export/import-export.service';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
|
@ -69,6 +70,7 @@ const DECLARATIONS = [
|
||||||
SavingWorkbasketService,
|
SavingWorkbasketService,
|
||||||
ClassificationsService,
|
ClassificationsService,
|
||||||
ClassificationCategoriesService,
|
ClassificationCategoriesService,
|
||||||
|
ImportExportService,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AdministrationModule {
|
export class AdministrationModule {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import { AlertService } from 'app/services/alert/alert.service';
|
||||||
import { TreeService } from 'app/services/tree/tree.service';
|
import { TreeService } from 'app/services/tree/tree.service';
|
||||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||||
import { RemoveConfirmationService } from 'app/services/remove-confirmation/remove-confirmation.service';
|
import { RemoveConfirmationService } from 'app/services/remove-confirmation/remove-confirmation.service';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-dummy-detail',
|
selector: 'taskana-dummy-detail',
|
||||||
|
|
@ -52,8 +53,7 @@ describe('ClassificationDetailsComponent', () => {
|
||||||
imports: [FormsModule, HttpClientModule, RouterTestingModule.withRoutes(routes), AngularSvgIconModule],
|
imports: [FormsModule, HttpClientModule, RouterTestingModule.withRoutes(routes), AngularSvgIconModule],
|
||||||
declarations: [ClassificationDetailsComponent, DummyDetailComponent],
|
declarations: [ClassificationDetailsComponent, DummyDetailComponent],
|
||||||
providers: [MasterAndDetailService, RequestInProgressService, ClassificationsService, HttpClient, ErrorModalService, AlertService,
|
providers: [MasterAndDetailService, RequestInProgressService, ClassificationsService, HttpClient, ErrorModalService, AlertService,
|
||||||
TreeService, ClassificationCategoriesService,
|
TreeService, ClassificationCategoriesService, CustomFieldsService, ImportExportService]
|
||||||
CustomFieldsService]
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
configureTests(configure).then(testBed => {
|
configureTests(configure).then(testBed => {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import { CustomFieldsService } from '../../../services/custom-fields/custom-fiel
|
||||||
import { Pair } from 'app/models/pair';
|
import { Pair } from 'app/models/pair';
|
||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
import { FormsValidatorService } from 'app/shared/services/forms/forms-validator.service';
|
import { FormsValidatorService } from 'app/shared/services/forms/forms-validator.service';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-classification-details',
|
selector: 'taskana-classification-details',
|
||||||
|
|
@ -37,7 +38,6 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
classification: ClassificationDefinition;
|
classification: ClassificationDefinition;
|
||||||
classificationClone: ClassificationDefinition;
|
classificationClone: ClassificationDefinition;
|
||||||
selectedClassification: ClassificationDefinition = undefined;
|
|
||||||
showDetail = false;
|
showDetail = false;
|
||||||
classificationTypes: Array<string> = [];
|
classificationTypes: Array<string> = [];
|
||||||
badgeMessage = '';
|
badgeMessage = '';
|
||||||
|
|
@ -65,6 +65,7 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||||
private selectedClassificationTypeSubscription: Subscription;
|
private selectedClassificationTypeSubscription: Subscription;
|
||||||
private categoriesSubscription: Subscription;
|
private categoriesSubscription: Subscription;
|
||||||
private domainSubscription: Subscription;
|
private domainSubscription: Subscription;
|
||||||
|
private importingExportingSubscription: Subscription;
|
||||||
|
|
||||||
@ViewChild('ClassificationForm') classificationForm: NgForm;
|
@ViewChild('ClassificationForm') classificationForm: NgForm;
|
||||||
toogleValidationMap = new Map<string, boolean>();
|
toogleValidationMap = new Map<string, boolean>();
|
||||||
|
|
@ -81,7 +82,8 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||||
private domainService: DomainService,
|
private domainService: DomainService,
|
||||||
private customFieldsService: CustomFieldsService,
|
private customFieldsService: CustomFieldsService,
|
||||||
private removeConfirmationService: RemoveConfirmationService,
|
private removeConfirmationService: RemoveConfirmationService,
|
||||||
private formsValidatorService: FormsValidatorService) {
|
private formsValidatorService: FormsValidatorService,
|
||||||
|
private importExportService: ImportExportService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
@ -107,7 +109,7 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||||
if (id === 'undefined') {
|
if (id === 'undefined') {
|
||||||
id = undefined;
|
id = undefined;
|
||||||
}
|
}
|
||||||
this.fillClassificationInformation(this.selectedClassification ? this.selectedClassification : new ClassificationDefinition())
|
this.fillClassificationInformation(this.classification ? this.classification : new ClassificationDefinition())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.classification || this.classification.classificationId !== id && id && id !== '') {
|
if (!this.classification || this.classification.classificationId !== id && id && id !== '') {
|
||||||
|
|
@ -125,6 +127,10 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||||
this.classification.category = categories[0];
|
this.classification.category = categories[0];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.importingExportingSubscription = this.importExportService.getImportingFinished().subscribe((value: Boolean) => {
|
||||||
|
if (this.classification.classificationId) { this.selectClassification(this.classification.classificationId); }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
backClicked(): void {
|
backClicked(): void {
|
||||||
|
|
@ -216,14 +222,14 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
this.requestInProgress = true;
|
this.requestInProgress = true;
|
||||||
this.selectedClassificationSubscription = this.classificationsService.getClassification(id).subscribe(classification => {
|
this.selectedClassificationSubscription = this.classificationsService.getClassification(id).subscribe(classification => {
|
||||||
this.selectedClassification = classification;
|
this.fillClassificationInformation(classification)
|
||||||
this.classificationsService.selectClassification(classification);
|
this.classificationsService.selectClassification(classification);
|
||||||
this.requestInProgress = false;
|
this.requestInProgress = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private classificationIsAlreadySelected(): boolean {
|
private classificationIsAlreadySelected(): boolean {
|
||||||
if (this.action === ACTION.CREATE && this.selectedClassification) { return true }
|
if (this.action === ACTION.CREATE && this.classification) { return true }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fillClassificationInformation(classificationSelected: ClassificationDefinition) {
|
private fillClassificationInformation(classificationSelected: ClassificationDefinition) {
|
||||||
|
|
@ -287,7 +293,7 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private cloneClassification (classification: ClassificationDefinition) {
|
private cloneClassification(classification: ClassificationDefinition) {
|
||||||
this.classificationClone = { ...classification };
|
this.classificationClone = { ...classification };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,5 +317,6 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy {
|
||||||
if (this.selectedClassificationTypeSubscription) { this.selectedClassificationTypeSubscription.unsubscribe(); }
|
if (this.selectedClassificationTypeSubscription) { this.selectedClassificationTypeSubscription.unsubscribe(); }
|
||||||
if (this.categoriesSubscription) { this.categoriesSubscription.unsubscribe(); }
|
if (this.categoriesSubscription) { this.categoriesSubscription.unsubscribe(); }
|
||||||
if (this.domainSubscription) { this.domainSubscription.unsubscribe(); }
|
if (this.domainSubscription) { this.domainSubscription.unsubscribe(); }
|
||||||
|
if (this.importingExportingSubscription) { this.importingExportingSubscription.unsubscribe(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<button type="button" (click)="addClassification()" data-toggle="tooltip" title="Add" class="btn btn-default">
|
<button type="button" (click)="addClassification()" data-toggle="tooltip" title="Add" class="btn btn-default">
|
||||||
<span class="material-icons md-20 green-blue">add_circle_outline</span>
|
<span class="material-icons md-20 green-blue">add_circle_outline</span>
|
||||||
</button>
|
</button>
|
||||||
<taskana-import-export-component class ="btn-group" [currentSelection]="selectionToImport" (importSucessful)="refreshClassificationList()">
|
<taskana-import-export-component class ="btn-group" [currentSelection]="selectionToImport">
|
||||||
</taskana-import-export-component>
|
</taskana-import-export-component>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import {
|
||||||
} from 'app/services/classifications/classification-categories.service';
|
} from 'app/services/classifications/classification-categories.service';
|
||||||
import { Pair } from 'app/models/pair';
|
import { Pair } from 'app/models/pair';
|
||||||
import { TreeService } from 'app/services/tree/tree.service';
|
import { TreeService } from 'app/services/tree/tree.service';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-dummy-detail',
|
selector: 'taskana-dummy-detail',
|
||||||
|
|
@ -54,7 +55,7 @@ describe('ClassificationListComponent', () => {
|
||||||
imports: [HttpClientModule, RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule],
|
imports: [HttpClientModule, RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule],
|
||||||
providers: [
|
providers: [
|
||||||
HttpClient, WorkbasketDefinitionService, AlertService, ClassificationsService, DomainService, ClassificationDefinitionService,
|
HttpClient, WorkbasketDefinitionService, AlertService, ClassificationsService, DomainService, ClassificationDefinitionService,
|
||||||
ErrorModalService, RequestInProgressService, ClassificationCategoriesService, TreeService
|
ErrorModalService, RequestInProgressService, ClassificationCategoriesService, TreeService, ImportExportService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
} from 'app/services/classifications/classification-categories.service';
|
} from 'app/services/classifications/classification-categories.service';
|
||||||
import { Pair } from 'app/models/pair';
|
import { Pair } from 'app/models/pair';
|
||||||
import { ClassificationDefinition } from '../../../../models/classification-definition';
|
import { ClassificationDefinition } from '../../../../models/classification-definition';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-classification-list',
|
selector: 'taskana-classification-list',
|
||||||
|
|
@ -37,12 +38,14 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
|
||||||
classificationSavedSubscription: Subscription;
|
classificationSavedSubscription: Subscription;
|
||||||
selectedClassificationSubscription: Subscription;
|
selectedClassificationSubscription: Subscription;
|
||||||
categoriesSubscription: Subscription;
|
categoriesSubscription: Subscription;
|
||||||
|
importingExportingSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private classificationService: ClassificationsService,
|
private classificationService: ClassificationsService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private categoryService: ClassificationCategoriesService) {
|
private categoryService: ClassificationCategoriesService,
|
||||||
|
private importExportService: ImportExportService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
@ -52,14 +55,17 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
|
||||||
this.performRequest(true);
|
this.performRequest(true);
|
||||||
});
|
});
|
||||||
this.selectedClassificationSubscription = this.categoryService.getSelectedClassificationType().subscribe(value => {
|
this.selectedClassificationSubscription = this.categoryService.getSelectedClassificationType().subscribe(value => {
|
||||||
this.classificationTypeSelected = value;
|
this.classificationTypeSelected = value;
|
||||||
this.performRequest();
|
this.performRequest();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.categoriesSubscription =
|
this.categoriesSubscription =
|
||||||
this.categoryService.getCategories(this.classificationTypeSelected).subscribe((categories: Array<string>) => {
|
this.categoryService.getCategories(this.classificationTypeSelected).subscribe((categories: Array<string>) => {
|
||||||
this.categories = categories;
|
this.categories = categories;
|
||||||
});
|
});
|
||||||
|
this.importingExportingSubscription = this.importExportService.getImportingFinished().subscribe((value: Boolean) => {
|
||||||
|
this.performRequest(true);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
selectClassificationType(classificationTypeSelected: string) {
|
selectClassificationType(classificationTypeSelected: string) {
|
||||||
|
|
@ -70,8 +76,8 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
|
||||||
.subscribe((classifications: Array<TreeNodeModel>) => {
|
.subscribe((classifications: Array<TreeNodeModel>) => {
|
||||||
this.classifications = classifications;
|
this.classifications = classifications;
|
||||||
this.requestInProgress = false;
|
this.requestInProgress = false;
|
||||||
});
|
});
|
||||||
this.selectClassification(undefined);
|
this.selectClassification(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectClassification(id: string) {
|
selectClassification(id: string) {
|
||||||
|
|
@ -123,16 +129,11 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshClassificationList() {
|
|
||||||
this.performRequest(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.classificationServiceSubscription) { this.classificationServiceSubscription.unsubscribe(); }
|
if (this.classificationServiceSubscription) { this.classificationServiceSubscription.unsubscribe(); }
|
||||||
if (this.classificationTypeServiceSubscription) { this.classificationTypeServiceSubscription.unsubscribe(); }
|
if (this.classificationTypeServiceSubscription) { this.classificationTypeServiceSubscription.unsubscribe(); }
|
||||||
if (this.classificationSelectedSubscription) { this.classificationSelectedSubscription.unsubscribe(); }
|
if (this.classificationSelectedSubscription) { this.classificationSelectedSubscription.unsubscribe(); }
|
||||||
if (this.classificationSavedSubscription) { this.classificationSavedSubscription.unsubscribe(); }
|
if (this.classificationSavedSubscription) { this.classificationSavedSubscription.unsubscribe(); }
|
||||||
|
if (this.importingExportingSubscription) { this.importingExportingSubscription.unsubscribe(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
<input #selectedFile type="file" accept=".json" (change)="onSelectFile($event)" class="hide" />
|
|
||||||
<button type="button" (click)="selectedFile.click()" data-toggle="tooltip" title="Import" class="btn btn-default">
|
<button type="button" [ngClass]="{disabled: uploadservice?.isInUse}" (click)="selectedFile.click()" data-toggle="tooltip" title="Import" class="btn btn-default">
|
||||||
<span class="material-icons md-20 green-blue">cloud_upload</span>
|
<span class="material-icons md-20 green-blue">cloud_upload</span>
|
||||||
</button>
|
</button>
|
||||||
|
<form class="hidden" id="upload_form" enctype="multipart/form-data" method="post">
|
||||||
|
<input #selectedFile type="file" accept=".json" (change)="uploadFile()" class="hide" />
|
||||||
|
</form>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript:void(0)" class="dropdown-item" (click)="export()">
|
<a href="javascript:void(0)" class="dropdown-item" (click)="export()">
|
||||||
<label>All Domains</label>
|
<label>All Domains</label>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<div role="separator" class="divider"></div>
|
<div role="separator" class="divider"></div>
|
||||||
<li *ngFor="let domain of domains">
|
<li *ngFor="let domain of domains">
|
||||||
<a href="javascript:void(0)" class="dropdown-item" (click)="export(domain)">
|
<a href="javascript:void(0)" class="dropdown-item" (click)="export(domain)">
|
||||||
<label>{{domain === '' ? 'Master' : domain}}</label>
|
<label>{{domain === '' ? 'Master' : domain}}</label>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<button type="button" data-toggle="dropdown" title="Export" class="btn btn-default">
|
<button [ngClass]="{disabled: uploadservice?.isInUse}" type="button" data-toggle="dropdown" title="Export" class="btn btn-default">
|
||||||
<span class="material-icons md-20 red">cloud_download</span>
|
<span class="material-icons md-20 red">cloud_download</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { HttpClientModule } from '@angular/common/http';
|
||||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||||
import { configureTests } from 'app/app.test.configuration';
|
import { configureTests } from 'app/app.test.configuration';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
describe('ImportExportComponent', () => {
|
describe('ImportExportComponent', () => {
|
||||||
let component: ImportExportComponent;
|
let component: ImportExportComponent;
|
||||||
|
|
@ -21,7 +22,7 @@ describe('ImportExportComponent', () => {
|
||||||
declarations: [ImportExportComponent],
|
declarations: [ImportExportComponent],
|
||||||
imports: [HttpClientModule, AngularSvgIconModule],
|
imports: [HttpClientModule, AngularSvgIconModule],
|
||||||
providers: [WorkbasketService, ClassificationDefinitionService, WorkbasketDefinitionService, AlertService,
|
providers: [WorkbasketService, ClassificationDefinitionService, WorkbasketDefinitionService, AlertService,
|
||||||
ErrorModalService]
|
ErrorModalService, ImportExportService]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
configureTests(configure).then(testBed => {
|
configureTests(configure).then(testBed => {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
|
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||||
import { ClassificationDefinitionService } from 'app/administration/services/classification-definition/classification-definition.service';
|
import { ClassificationDefinitionService } from 'app/administration/services/classification-definition/classification-definition.service';
|
||||||
import { WorkbasketDefinitionService } from 'app/administration/services/workbasket-definition/workbasket-definition.service';
|
import { WorkbasketDefinitionService } from 'app/administration/services/workbasket-definition/workbasket-definition.service';
|
||||||
import { DomainService } from 'app/services/domain/domain.service';
|
import { DomainService } from 'app/services/domain/domain.service';
|
||||||
import { TaskanaType } from 'app/models/taskana-type';
|
import { TaskanaType } from 'app/models/taskana-type';
|
||||||
import { ErrorModel } from 'app/models/modal-error';
|
import { ErrorModel } from 'app/models/modal-error';
|
||||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||||
|
import { environment } from 'environments/environment';
|
||||||
|
import { AlertService } from 'app/services/alert/alert.service';
|
||||||
|
import { AlertModel, AlertType } from 'app/models/alert';
|
||||||
|
import { UploadService } from 'app/shared/services/upload/upload.service';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-import-export-component',
|
selector: 'taskana-import-export-component',
|
||||||
|
|
@ -15,13 +20,20 @@ export class ImportExportComponent implements OnInit {
|
||||||
|
|
||||||
@Input() currentSelection: TaskanaType;
|
@Input() currentSelection: TaskanaType;
|
||||||
|
|
||||||
@Output() importSucessful = new EventEmitter();
|
@ViewChild('selectedFile')
|
||||||
|
selectedFileInput;
|
||||||
|
|
||||||
domains: string[] = [];
|
domains: string[] = [];
|
||||||
|
errorWhileUploadingText: string;
|
||||||
|
|
||||||
constructor(private domainService: DomainService, private workbasketDefinitionService: WorkbasketDefinitionService,
|
constructor(
|
||||||
private classificationDefinitionService: ClassificationDefinitionService, private errorModalService: ErrorModalService) {
|
private domainService: DomainService,
|
||||||
|
private workbasketDefinitionService: WorkbasketDefinitionService,
|
||||||
|
private classificationDefinitionService: ClassificationDefinitionService,
|
||||||
|
private errorModalService: ErrorModalService,
|
||||||
|
private alertService: AlertService,
|
||||||
|
public uploadservice: UploadService,
|
||||||
|
private importExportService: ImportExportService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
@ -30,30 +42,6 @@ export class ImportExportComponent implements OnInit {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectFile(event) {
|
|
||||||
const file = event.target.files[0];
|
|
||||||
|
|
||||||
const ending = file.name.match(/\.([^\.]+)$/)[1];
|
|
||||||
switch (ending) {
|
|
||||||
case 'json':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
file.value = '';
|
|
||||||
this.errorModalService.triggerError(new ErrorModel(undefined,
|
|
||||||
`This file format is not allowed! Please use a .json file.`));
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
if (this.currentSelection === TaskanaType.WORKBASKETS) {
|
|
||||||
reader.onload = <Event>(e) => this.workbasketDefinitionService.importWorkbasketDefinitions(e.target.result);
|
|
||||||
this.importSucessful.emit();
|
|
||||||
} else {
|
|
||||||
reader.onload = <Event>(e) => this.classificationDefinitionService.importClassifications(e.target.result);
|
|
||||||
this.importSucessful.emit();
|
|
||||||
}
|
|
||||||
reader.readAsText(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
export(domain = '') {
|
export(domain = '') {
|
||||||
if (this.currentSelection === TaskanaType.WORKBASKETS) {
|
if (this.currentSelection === TaskanaType.WORKBASKETS) {
|
||||||
this.workbasketDefinitionService.exportWorkbaskets(domain);
|
this.workbasketDefinitionService.exportWorkbaskets(domain);
|
||||||
|
|
@ -61,4 +49,81 @@ export class ImportExportComponent implements OnInit {
|
||||||
this.classificationDefinitionService.exportClassifications(domain);
|
this.classificationDefinitionService.exportClassifications(domain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uploadFile() {
|
||||||
|
const file = this.selectedFileInput.nativeElement.files[0],
|
||||||
|
formdata = new FormData(),
|
||||||
|
ajax = new XMLHttpRequest();
|
||||||
|
if (!this.checkFormatFile(file)) { return false }
|
||||||
|
formdata.append('file', file);
|
||||||
|
ajax.upload.addEventListener('progress', this.progressHandler.bind(this), false);
|
||||||
|
ajax.addEventListener('load', this.resetProgress.bind(this), false);
|
||||||
|
ajax.addEventListener('error', this.errorHandler.bind(this), false);
|
||||||
|
ajax.onreadystatechange = this.onReadyStateChangeHandler.bind(this, ajax);
|
||||||
|
if (this.currentSelection === TaskanaType.WORKBASKETS) {
|
||||||
|
ajax.open('POST', environment.taskanaRestUrl + '/v1/workbasket-definitions');
|
||||||
|
} else {
|
||||||
|
ajax.open('POST', environment.taskanaRestUrl + '/v1/classification-definitions');
|
||||||
|
}
|
||||||
|
ajax.send(formdata);
|
||||||
|
this.uploadservice.isInUse = true;
|
||||||
|
this.uploadservice.setCurrentProgressValue(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
progressHandler(event) {
|
||||||
|
const percent = (event.loaded / event.total) * 100;
|
||||||
|
this.uploadservice.setCurrentProgressValue(Math.round(percent))
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkFormatFile(file): boolean {
|
||||||
|
const ending = file.name.match(/\.([^\.]+)$/)[1];
|
||||||
|
let check = false;
|
||||||
|
switch (ending) {
|
||||||
|
case 'json':
|
||||||
|
check = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
file.value = '';
|
||||||
|
this.errorModalService.triggerError(new ErrorModel(undefined,
|
||||||
|
`This file format is not allowed! Please use a .json file.`));
|
||||||
|
}
|
||||||
|
return check;
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetProgress() {
|
||||||
|
this.uploadservice.setCurrentProgressValue(0)
|
||||||
|
this.uploadservice.isInUse = false;
|
||||||
|
this.selectedFileInput.nativeElement.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private onReadyStateChangeHandler(event) {
|
||||||
|
|
||||||
|
if (event.readyState === 4 && event.status >= 400) {
|
||||||
|
let title;
|
||||||
|
if (event.status === 401) {
|
||||||
|
title = 'Import was not successful, you have no access to apply this operation.';
|
||||||
|
} else if (event.status === 404) {
|
||||||
|
title = 'Import was not successful, operation was not found.';
|
||||||
|
} else if (event.status === 409) {
|
||||||
|
title = 'Import was not successful, operation has some conflicts.';
|
||||||
|
}
|
||||||
|
this.errorHandler(title, JSON.parse(event.responseText).message);
|
||||||
|
|
||||||
|
} else if (event.readyState === 4 && event.status === 200) {
|
||||||
|
this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS, 'Import was successful'))
|
||||||
|
this.importExportService.setImportingFinished(true);
|
||||||
|
this.resetProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private errorHandler(title = 'Import was not successful', message) {
|
||||||
|
this.errorModalService.triggerError(
|
||||||
|
new ErrorModel(
|
||||||
|
title,
|
||||||
|
message
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.selectedFileInput.files = undefined;
|
||||||
|
this.resetProgress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { environment } from 'app/../environments/environment';
|
import { environment } from 'app/../environments/environment';
|
||||||
import { AlertService } from 'app/services/alert/alert.service';
|
|
||||||
import { ClassificationDefinition } from 'app/models/classification-definition';
|
import { ClassificationDefinition } from 'app/models/classification-definition';
|
||||||
import { AlertModel, AlertType } from 'app/models/alert';
|
|
||||||
import { saveAs } from 'file-saver/FileSaver';
|
import { saveAs } from 'file-saver/FileSaver';
|
||||||
import { TaskanaDate } from 'app/shared/util/taskana.date';
|
import { TaskanaDate } from 'app/shared/util/taskana.date';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ClassificationDefinitionService {
|
export class ClassificationDefinitionService {
|
||||||
|
|
||||||
url = environment.taskanaRestUrl + '/v1/classificationdefinitions';
|
url = environment.taskanaRestUrl + '/v1/classification-definitions';
|
||||||
constructor(
|
constructor(
|
||||||
private httpClient: HttpClient,
|
private httpClient: HttpClient
|
||||||
private alertService: AlertService
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,14 +23,4 @@ export class ClassificationDefinitionService {
|
||||||
'Classifications_' + TaskanaDate.getDate() + '.json')
|
'Classifications_' + TaskanaDate.getDate() + '.json')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST
|
|
||||||
// TODO handle error
|
|
||||||
importClassifications(classifications: any) {
|
|
||||||
this.httpClient.post(this.url,
|
|
||||||
JSON.parse(classifications)).subscribe(
|
|
||||||
classificationsUpdated => this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS, 'Import was successful')),
|
|
||||||
error => this.alertService.triggerAlert(new AlertModel(AlertType.DANGER, 'Import was not successful'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Subject, Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ImportExportService {
|
||||||
|
|
||||||
|
public importingFinished = new Subject<boolean>();
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
setImportingFinished(value: boolean) {
|
||||||
|
this.importingFinished.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
getImportingFinished(): Observable<boolean> {
|
||||||
|
return this.importingFinished.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,15 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { environment } from 'app/../environments/environment';
|
import { environment } from 'app/../environments/environment';
|
||||||
import { saveAs } from 'file-saver/FileSaver';
|
import { saveAs } from 'file-saver/FileSaver';
|
||||||
import { AlertService } from 'app/services/alert/alert.service';
|
|
||||||
import { WorkbasketDefinition } from 'app/models/workbasket-definition';
|
import { WorkbasketDefinition } from 'app/models/workbasket-definition';
|
||||||
import { AlertModel, AlertType } from 'app/models/alert';
|
|
||||||
import { TaskanaDate } from 'app/shared/util/taskana.date';
|
import { TaskanaDate } from 'app/shared/util/taskana.date';
|
||||||
import { ErrorModel } from 'app/models/modal-error';
|
|
||||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkbasketDefinitionService {
|
export class WorkbasketDefinitionService {
|
||||||
url: string = environment.taskanaRestUrl + '/v1/workbasketdefinitions';
|
url: string = environment.taskanaRestUrl + '/v1/workbasket-definitions';
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient, private alertService: AlertService,
|
constructor(private httpClient: HttpClient) {
|
||||||
private errorModalService: ErrorModalService) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET
|
// GET
|
||||||
|
|
@ -28,14 +22,4 @@ export class WorkbasketDefinitionService {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST
|
|
||||||
importWorkbasketDefinitions(workbasketDefinitions: any) {
|
|
||||||
this.httpClient.post(environment.taskanaRestUrl + '/v1/workbasketdefinitions',
|
|
||||||
JSON.parse(workbasketDefinitions)).subscribe(
|
|
||||||
workbasketsUpdated => this.alertService.triggerAlert(new AlertModel(AlertType.SUCCESS, 'Import was successful')),
|
|
||||||
error => this.errorModalService.triggerError(new ErrorModel(
|
|
||||||
`There was an error importing workbaskets`, error.message))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import { AccessItemsComponent } from './access-items/access-items.component';
|
||||||
import { DistributionTargetsComponent } from './distribution-targets/distribution-targets.component';
|
import { DistributionTargetsComponent } from './distribution-targets/distribution-targets.component';
|
||||||
import { DualListComponent } from './distribution-targets//dual-list/dual-list.component';
|
import { DualListComponent } from './distribution-targets//dual-list/dual-list.component';
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-dummy-detail',
|
selector: 'taskana-dummy-detail',
|
||||||
|
|
@ -56,14 +57,13 @@ describe('WorkbasketDetailsComponent', () => {
|
||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
const configure = (testBed: TestBed) => {
|
const configure = (testBed: TestBed) => {
|
||||||
testBed.configureTestingModule({
|
testBed.configureTestingModule({
|
||||||
imports: [RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, HttpClientModule, ReactiveFormsModule,
|
imports: [RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, HttpClientModule, ReactiveFormsModule,
|
||||||
InfiniteScrollModule],
|
InfiniteScrollModule],
|
||||||
declarations: [WorkbasketDetailsComponent, WorkbasketInformationComponent,
|
declarations: [WorkbasketDetailsComponent, WorkbasketInformationComponent,
|
||||||
AccessItemsComponent,
|
AccessItemsComponent,
|
||||||
DistributionTargetsComponent, DualListComponent, DummyDetailComponent],
|
DistributionTargetsComponent, DualListComponent, DummyDetailComponent],
|
||||||
providers: [WorkbasketService, MasterAndDetailService, ErrorModalService, RequestInProgressService,
|
providers: [WorkbasketService, MasterAndDetailService, ErrorModalService, RequestInProgressService,
|
||||||
AlertService, SavingWorkbasketService,
|
AlertService, SavingWorkbasketService, CustomFieldsService, ImportExportService]
|
||||||
CustomFieldsService]
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
configureTests(configure).then(testBed => {
|
configureTests(configure).then(testBed => {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { WorkbasketService } from 'app/services/workbasket/workbasket.service'
|
||||||
import { MasterAndDetailService } from 'app/services/masterAndDetail/master-and-detail.service'
|
import { MasterAndDetailService } from 'app/services/masterAndDetail/master-and-detail.service'
|
||||||
import { DomainService } from 'app/services/domain/domain.service';
|
import { DomainService } from 'app/services/domain/domain.service';
|
||||||
import { ErrorModalService } from '../../../services/errorModal/error-modal.service';
|
import { ErrorModalService } from '../../../services/errorModal/error-modal.service';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-workbasket-details',
|
selector: 'taskana-workbasket-details',
|
||||||
|
|
@ -18,7 +18,6 @@ import { ErrorModalService } from '../../../services/errorModal/error-modal.serv
|
||||||
})
|
})
|
||||||
export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
|
||||||
workbasket: Workbasket;
|
workbasket: Workbasket;
|
||||||
workbasketCopy: Workbasket;
|
workbasketCopy: Workbasket;
|
||||||
selectedId: string = undefined;
|
selectedId: string = undefined;
|
||||||
|
|
@ -33,13 +32,15 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
||||||
private masterAndDetailSubscription: Subscription;
|
private masterAndDetailSubscription: Subscription;
|
||||||
private permissionSubscription: Subscription;
|
private permissionSubscription: Subscription;
|
||||||
private domainSubscription: Subscription;
|
private domainSubscription: Subscription;
|
||||||
|
private importingExportingSubscription: Subscription;
|
||||||
|
|
||||||
constructor(private service: WorkbasketService,
|
constructor(private service: WorkbasketService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private masterAndDetailService: MasterAndDetailService,
|
private masterAndDetailService: MasterAndDetailService,
|
||||||
private domainService: DomainService,
|
private domainService: DomainService,
|
||||||
private errorModalService: ErrorModalService) { }
|
private errorModalService: ErrorModalService,
|
||||||
|
private importExportService: ImportExportService) { }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -77,6 +78,10 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
||||||
this.masterAndDetailSubscription = this.masterAndDetailService.getShowDetail().subscribe(showDetail => {
|
this.masterAndDetailSubscription = this.masterAndDetailService.getShowDetail().subscribe(showDetail => {
|
||||||
this.showDetail = showDetail;
|
this.showDetail = showDetail;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.importingExportingSubscription = this.importExportService.getImportingFinished().subscribe((value: Boolean) => {
|
||||||
|
if (this.workbasket) { this.getWorkbasketInformation(this.workbasket.workbasketId); }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
backClicked(): void {
|
backClicked(): void {
|
||||||
|
|
@ -113,9 +118,9 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
||||||
this.requestInProgress = false;
|
this.requestInProgress = false;
|
||||||
this.checkDomainAndRedirect();
|
this.checkDomainAndRedirect();
|
||||||
}, err => {
|
}, err => {
|
||||||
this.errorModalService.triggerError(
|
this.errorModalService.triggerError(
|
||||||
new ErrorModel('An error occurred while fetching the workbasket', err));
|
new ErrorModel('An error occurred while fetching the workbasket', err));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,5 +139,6 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
||||||
if (this.masterAndDetailSubscription) { this.masterAndDetailSubscription.unsubscribe(); }
|
if (this.masterAndDetailSubscription) { this.masterAndDetailSubscription.unsubscribe(); }
|
||||||
if (this.permissionSubscription) { this.permissionSubscription.unsubscribe(); }
|
if (this.permissionSubscription) { this.permissionSubscription.unsubscribe(); }
|
||||||
if (this.domainSubscription) { this.domainSubscription.unsubscribe(); }
|
if (this.domainSubscription) { this.domainSubscription.unsubscribe(); }
|
||||||
|
if (this.importingExportingSubscription) { this.importingExportingSubscription.unsubscribe(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<li id="wb-action-toolbar" class="list-group-item tab-align">
|
<li id="wb-action-toolbar" class="list-group-item tab-align">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9 btn-group">
|
<div class="col-xs-7 btn-group">
|
||||||
<button type="button" (click)="addWorkbasket()" data-toggle="tooltip" title="Add" class="btn btn-default">
|
<button type="button" (click)="addWorkbasket()" data-toggle="tooltip" title="Add" class="btn btn-default">
|
||||||
<span class="material-icons md-20 green-blue">add_circle_outline</span>
|
<span class="material-icons md-20 green-blue">add_circle_outline</span>
|
||||||
</button>
|
</button>
|
||||||
<taskana-import-export-component class="btn-group" (importSucessful)="importEvent()" [currentSelection]="selectionToImport"></taskana-import-export-component>
|
<taskana-import-export-component class="btn-group" [currentSelection]="selectionToImport"></taskana-import-export-component>
|
||||||
</div>
|
</div>
|
||||||
<div class="margin-right pull-right btn-group">
|
<div class="margin-right pull-right btn-group">
|
||||||
<taskana-sort [sortingFields]="sortingFields" (performSorting)="sorting($event)" class="btn-group"></taskana-sort>
|
<taskana-sort [sortingFields]="sortingFields" (performSorting)="sorting($event)" class="btn-group"></taskana-sort>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||||
import { ClassificationDefinitionService } from 'app/administration/services/classification-definition/classification-definition.service';
|
import { ClassificationDefinitionService } from 'app/administration/services/classification-definition/classification-definition.service';
|
||||||
import { WorkbasketDefinitionService } from 'app/administration/services/workbasket-definition/workbasket-definition.service';
|
import { WorkbasketDefinitionService } from 'app/administration/services/workbasket-definition/workbasket-definition.service';
|
||||||
import { configureTests } from 'app/app.test.configuration';
|
import { configureTests } from 'app/app.test.configuration';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-dummy-detail',
|
selector: 'taskana-dummy-detail',
|
||||||
|
|
@ -50,6 +51,7 @@ describe('WorkbasketListToolbarComponent', () => {
|
||||||
WorkbasketService,
|
WorkbasketService,
|
||||||
ClassificationDefinitionService,
|
ClassificationDefinitionService,
|
||||||
WorkbasketDefinitionService,
|
WorkbasketDefinitionService,
|
||||||
|
ImportExportService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
||||||
@Input() workbaskets: Array<WorkbasketSummary>;
|
@Input() workbaskets: Array<WorkbasketSummary>;
|
||||||
@Output() performSorting = new EventEmitter<SortingModel>();
|
@Output() performSorting = new EventEmitter<SortingModel>();
|
||||||
@Output() performFilter = new EventEmitter<FilterModel>();
|
@Output() performFilter = new EventEmitter<FilterModel>();
|
||||||
@Output() importSucessful = new EventEmitter();
|
|
||||||
workbasketServiceSubscription: Subscription;
|
workbasketServiceSubscription: Subscription;
|
||||||
selectionToImport = TaskanaType.WORKBASKETS;
|
selectionToImport = TaskanaType.WORKBASKETS;
|
||||||
sortingFields = new Map([['name', 'Name'], ['key', 'Key'], ['description', 'Description'], ['owner', 'Owner'], ['type', 'Type']]);
|
sortingFields = new Map([['name', 'Name'], ['key', 'Key'], ['description', 'Description'], ['owner', 'Owner'], ['type', 'Type']]);
|
||||||
|
|
@ -54,7 +53,4 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
||||||
this.router.navigate([{ outlets: { detail: ['new-workbasket'] } }], { relativeTo: this.route });
|
this.router.navigate([{ outlets: { detail: ['new-workbasket'] } }], { relativeTo: this.route });
|
||||||
}
|
}
|
||||||
|
|
||||||
importEvent() {
|
|
||||||
this.importSucessful.emit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { WorkbasketService } from 'app/services/workbasket/workbasket.service';
|
||||||
import { OrientationService } from 'app/services/orientation/orientation.service';
|
import { OrientationService } from 'app/services/orientation/orientation.service';
|
||||||
import { configureTests } from 'app/app.test.configuration';
|
import { configureTests } from 'app/app.test.configuration';
|
||||||
import { Page } from 'app/models/page';
|
import { Page } from 'app/models/page';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-dummy-detail',
|
selector: 'taskana-dummy-detail',
|
||||||
|
|
@ -80,7 +81,8 @@ describe('WorkbasketListComponent', () => {
|
||||||
WorkbasketService,
|
WorkbasketService,
|
||||||
WorkbasketDefinitionService,
|
WorkbasketDefinitionService,
|
||||||
ClassificationDefinitionService,
|
ClassificationDefinitionService,
|
||||||
OrientationService
|
OrientationService,
|
||||||
|
ImportExportService
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {Orientation} from 'app/models/orientation';
|
||||||
import {WorkbasketService} from 'app/services/workbasket/workbasket.service'
|
import {WorkbasketService} from 'app/services/workbasket/workbasket.service'
|
||||||
import {OrientationService} from 'app/services/orientation/orientation.service';
|
import {OrientationService} from 'app/services/orientation/orientation.service';
|
||||||
import {TaskanaQueryParameters} from 'app/shared/util/query-parameters';
|
import {TaskanaQueryParameters} from 'app/shared/util/query-parameters';
|
||||||
|
import { ImportExportService } from 'app/administration/services/import-export/import-export.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-workbasket-list',
|
selector: 'taskana-workbasket-list',
|
||||||
|
|
@ -38,12 +39,14 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
||||||
private workbasketServiceSubscription: Subscription;
|
private workbasketServiceSubscription: Subscription;
|
||||||
private workbasketServiceSavedSubscription: Subscription;
|
private workbasketServiceSavedSubscription: Subscription;
|
||||||
private orientationSubscription: Subscription;
|
private orientationSubscription: Subscription;
|
||||||
|
private importingExportingSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private workbasketService: WorkbasketService,
|
private workbasketService: WorkbasketService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private orientationService: OrientationService) {
|
private orientationService: OrientationService,
|
||||||
|
private importExportService: ImportExportService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
@ -64,6 +67,10 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
||||||
this.orientationSubscription = this.orientationService.getOrientation().subscribe((orientation: Orientation) => {
|
this.orientationSubscription = this.orientationService.getOrientation().subscribe((orientation: Orientation) => {
|
||||||
this.refreshWorkbasketList();
|
this.refreshWorkbasketList();
|
||||||
});
|
});
|
||||||
|
this.importingExportingSubscription = this.importExportService.getImportingFinished().subscribe((value: Boolean) => {
|
||||||
|
this.refreshWorkbasketList();
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selectWorkbasket(id: string) {
|
selectWorkbasket(id: string) {
|
||||||
|
|
@ -121,5 +128,8 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
||||||
this.orientationSubscription.unsubscribe();
|
this.orientationSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.importingExportingSubscription) {
|
||||||
|
this.importingExportingSubscription.unsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,6 @@
|
||||||
<taskana-spinner [isRunning]="requestInProgress" isModal=true></taskana-spinner>
|
<taskana-spinner [isRunning]="requestInProgress" isModal=true></taskana-spinner>
|
||||||
<taskana-alert></taskana-alert>
|
<taskana-alert></taskana-alert>
|
||||||
<taskana-remove-confirmation></taskana-remove-confirmation>
|
<taskana-remove-confirmation></taskana-remove-confirmation>
|
||||||
|
<taskana-progress-bar [ngClass]="{hidden: currentProgressValue === 0}" currentValue={{currentProgressValue}}></taskana-progress-bar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -9,6 +9,7 @@ import { RequestInProgressService } from './services/requestInProgress/request-i
|
||||||
import { OrientationService } from './services/orientation/orientation.service';
|
import { OrientationService } from './services/orientation/orientation.service';
|
||||||
import { SelectedRouteService } from './services/selected-route/selected-route';
|
import { SelectedRouteService } from './services/selected-route/selected-route';
|
||||||
import { FormsValidatorService } from 'app/shared/services/forms/forms-validator.service';
|
import { FormsValidatorService } from 'app/shared/services/forms/forms-validator.service';
|
||||||
|
import { UploadService } from './shared/services/upload/upload.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-root',
|
selector: 'taskana-root',
|
||||||
|
|
@ -24,11 +25,13 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
selectedRoute = '';
|
selectedRoute = '';
|
||||||
|
|
||||||
requestInProgress = false;
|
requestInProgress = false;
|
||||||
|
currentProgressValue = 0;
|
||||||
|
|
||||||
errorModalSubscription: Subscription;
|
errorModalSubscription: Subscription;
|
||||||
requestInProgressSubscription: Subscription;
|
requestInProgressSubscription: Subscription;
|
||||||
selectedRouteSubscription: Subscription;
|
selectedRouteSubscription: Subscription;
|
||||||
routerSubscription: Subscription;
|
routerSubscription: Subscription;
|
||||||
|
uploadingFileSubscription: Subscription;
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
onResize(event) {
|
onResize(event) {
|
||||||
|
|
@ -41,7 +44,8 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
private requestInProgressService: RequestInProgressService,
|
private requestInProgressService: RequestInProgressService,
|
||||||
private orientationService: OrientationService,
|
private orientationService: OrientationService,
|
||||||
private selectedRouteService: SelectedRouteService,
|
private selectedRouteService: SelectedRouteService,
|
||||||
private formsValidatorService: FormsValidatorService) {
|
private formsValidatorService: FormsValidatorService,
|
||||||
|
public uploadService: UploadService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
@ -73,6 +77,9 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
this.selectedRoute = value;
|
this.selectedRoute = value;
|
||||||
})
|
})
|
||||||
|
this.uploadingFileSubscription = this.uploadService.getCurrentProgressValue().subscribe(value => {
|
||||||
|
this.currentProgressValue = value;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|
@ -80,5 +87,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
if (this.errorModalSubscription) { this.errorModalSubscription.unsubscribe(); }
|
if (this.errorModalSubscription) { this.errorModalSubscription.unsubscribe(); }
|
||||||
if (this.requestInProgressSubscription) { this.requestInProgressSubscription.unsubscribe(); }
|
if (this.requestInProgressSubscription) { this.requestInProgressSubscription.unsubscribe(); }
|
||||||
if (this.selectedRouteSubscription) { this.selectedRouteSubscription.unsubscribe(); }
|
if (this.selectedRouteSubscription) { this.selectedRouteSubscription.unsubscribe(); }
|
||||||
|
if (this.uploadingFileSubscription) { this.uploadingFileSubscription.unsubscribe(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { TreeModule } from 'angular-tree-component';
|
import { TreeModule } from 'angular-tree-component';
|
||||||
import { SharedModule } from 'app/shared/shared.module';
|
import { SharedModule } from 'app/shared/shared.module';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Services
|
* Services
|
||||||
*/
|
*/
|
||||||
|
|
@ -34,6 +33,7 @@ import { WindowRefService } from 'app/services/window/window.service';
|
||||||
import { TaskanaEngineService } from 'app/services/taskana-engine/taskana-engine.service';
|
import { TaskanaEngineService } from 'app/services/taskana-engine/taskana-engine.service';
|
||||||
import { RemoveConfirmationService } from './services/remove-confirmation/remove-confirmation.service';
|
import { RemoveConfirmationService } from './services/remove-confirmation/remove-confirmation.service';
|
||||||
import { FormsValidatorService } from './shared/services/forms/forms-validator.service';
|
import { FormsValidatorService } from './shared/services/forms/forms-validator.service';
|
||||||
|
import { UploadService } from './shared/services/upload/upload.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
|
|
@ -110,7 +110,8 @@ export function startupServiceFactory(startupService: StartupService): () => Pro
|
||||||
CustomFieldsService,
|
CustomFieldsService,
|
||||||
TaskanaEngineService,
|
TaskanaEngineService,
|
||||||
RemoveConfirmationService,
|
RemoveConfirmationService,
|
||||||
FormsValidatorService
|
FormsValidatorService,
|
||||||
|
UploadService
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -71,5 +71,5 @@
|
||||||
<p id="taskana-version"> Taskana version: {{version}} </p>
|
<p id="taskana-version"> Taskana version: {{version}} </p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="showNavbar" class="navbar-backdrop" (click)="toogleNavBar()"></div>
|
<div *ngIf="showNavbar" class="backdrop" (click)="toogleNavBar()"></div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
@ -48,17 +48,6 @@ h2.navbar-brand{
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-backdrop{
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
top: 0;
|
|
||||||
z-index: 990;
|
|
||||||
background-color: grey;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.domain-form {
|
.domain-form {
|
||||||
margin: 13px;
|
margin: 13px;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Subject , Observable } from 'rxjs';
|
import { Subject , Observable } from 'rxjs';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RequestInProgressService {
|
export class RequestInProgressService {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,6 @@ ul.pagination{
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer{
|
.footer{
|
||||||
margin: 0 5px 0 0;
|
margin: 0px 5px 0 0;
|
||||||
color: $blue;
|
color: $blue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<div class="upload-file-container col-xs-12 col-md-4">
|
||||||
|
<div class="item progress-{{currentValue}}">
|
||||||
|
<div class="radial-inner-bg"><h1 class="blue"> {{currentValue}}%</h1></div>
|
||||||
|
</div>
|
||||||
|
<h4 class="blue">Uploading file</h4>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="currentValue" class="mask"></div>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
.upload-file-container{
|
||||||
|
z-index: 3000;
|
||||||
|
width: 170px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 33%;
|
||||||
|
margin-left: -85px;
|
||||||
|
> .item{
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProgressBarComponent } from './progress-bar.component';
|
||||||
|
|
||||||
|
describe('ProgressBarComponent', () => {
|
||||||
|
let component: ProgressBarComponent;
|
||||||
|
let fixture: ComponentFixture<ProgressBarComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ProgressBarComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProgressBarComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { Component, OnInit, Input, SimpleChanges, OnChanges } from '@angular/core';
|
||||||
|
@Component({
|
||||||
|
selector: 'taskana-progress-bar',
|
||||||
|
templateUrl: './progress-bar.component.html',
|
||||||
|
styleUrls: ['./progress-bar.component.scss']
|
||||||
|
})
|
||||||
|
export class ProgressBarComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
currentValue = 0;
|
||||||
|
@Input()
|
||||||
|
min = 0;
|
||||||
|
@Input()
|
||||||
|
max = 100;
|
||||||
|
|
||||||
|
inProgress = false;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (!this.inProgress && changes.currentValue.currentValue > this.min) {
|
||||||
|
this.inProgress = true;
|
||||||
|
}
|
||||||
|
if (this.inProgress && changes.currentValue.currentValue >= this.max) {
|
||||||
|
this.inProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class UploadService {
|
||||||
|
|
||||||
|
private currentProgressValue = new Subject<number>();
|
||||||
|
public isInUse = false;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
setCurrentProgressValue(value: number) {
|
||||||
|
this.currentProgressValue.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentProgressValue() {
|
||||||
|
return this.currentProgressValue.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ import { FilterComponent } from 'app/shared/filter/filter.component';
|
||||||
import { IconTypeComponent } from 'app/administration/components/type-icon/icon-type.component';
|
import { IconTypeComponent } from 'app/administration/components/type-icon/icon-type.component';
|
||||||
import { FieldErrorDisplayComponent } from 'app/shared/field-error-display/field-error-display.component';
|
import { FieldErrorDisplayComponent } from 'app/shared/field-error-display/field-error-display.component';
|
||||||
import { PaginationComponent } from './pagination/pagination.component';
|
import { PaginationComponent } from './pagination/pagination.component';
|
||||||
|
import { ProgressBarComponent } from './progress-bar/progress-bar.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pipes
|
* Pipes
|
||||||
|
|
@ -73,6 +74,7 @@ const DECLARATIONS = [
|
||||||
RemoveConfirmationComponent,
|
RemoveConfirmationComponent,
|
||||||
FieldErrorDisplayComponent,
|
FieldErrorDisplayComponent,
|
||||||
PaginationComponent,
|
PaginationComponent,
|
||||||
|
ProgressBarComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="footer-space-pagination-list">
|
<div class="footer-space-pagination-list">
|
||||||
<div #wbToolbar>
|
<div #wbToolbar>
|
||||||
<taskana-tasklist-toolbar (performSorting)="performSorting($event)" (performFilter)="performFilter($event)"
|
<taskana-tasklist-toolbar (performSorting)="performSorting($event)" (performFilter)="performFilter($event)"
|
||||||
(selectSearchType)="selectSearchType($event)" (importSucessful)="refreshWorkbasketList()">
|
(selectSearchType)="selectSearchType($event)">
|
||||||
</taskana-tasklist-toolbar>
|
</taskana-tasklist-toolbar>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!requestInProgress">
|
<div *ngIf="!requestInProgress">
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,8 @@
|
||||||
}
|
}
|
||||||
.btn-group ul + .btn, {
|
.btn-group ul + .btn, {
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-group > div.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,4 @@
|
||||||
@import 'tabs';
|
@import 'tabs';
|
||||||
@import 'bootstrap-3-backward-compatibility';
|
@import 'bootstrap-3-backward-compatibility';
|
||||||
@import 'mixin/colors';
|
@import 'mixin/colors';
|
||||||
|
@import 'progress-bar';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
@import '_colors';
|
||||||
|
.item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
position: relative;
|
||||||
|
width: 113px;
|
||||||
|
height: 113px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
background-color: #FFF;
|
||||||
|
float: left;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radial-inner-bg {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
top: 5px;
|
||||||
|
left: 5px;
|
||||||
|
background: #FFF;
|
||||||
|
position: absolute;
|
||||||
|
>h1 {
|
||||||
|
margin: 30px 0 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$step: 1; // step of % for created classes
|
||||||
|
$loops: 100;
|
||||||
|
$increment: (360 / $loops);
|
||||||
|
$half: round($loops / 2);
|
||||||
|
@for $i from 0 through $loops {
|
||||||
|
.progress-#{$i*$step} {
|
||||||
|
@if $i < 50 {
|
||||||
|
$nextdeg: 90deg + ( $increment * $i );
|
||||||
|
background-image: linear-gradient(90deg, #fff 50%, transparent 50%, transparent), linear-gradient($nextdeg, $pallete-green 51%, #fff 50%, #fff);
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
$nextdeg: -90deg + ( $increment * ( $i - $half ) );
|
||||||
|
background-image: linear-gradient($nextdeg, $pallete-green 51%, transparent 50%, transparent), linear-gradient(270deg, $pallete-green 50%, #fff 50%, #fff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -395,3 +395,25 @@ li.list-group-item:hover {
|
||||||
.padding-right.pull-right {
|
.padding-right.pull-right {
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.backdrop{
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 990;
|
||||||
|
background-color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mask {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2000;
|
||||||
|
background-color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue