diff --git a/.gitignore b/.gitignore
index 9b7f130217..17eca5e0b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,6 +28,9 @@
/.nb-gradle/
+### VS ###
+.vs
+
### VS Code ###
.vscode/
@@ -80,7 +83,7 @@ replay_pid*
logs/
target/
-.classpth
+.classpath
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.project
diff --git a/Dockerfile b/Dockerfile
index 38f77dceee..b2f6718c77 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,37 +1,37 @@
-# Settings.
+# Build arguments.
ARG USER_ID=3001
ARG USER_NAME=vireo
-ARG SOURCE_DIR=/$USER_NAME/source
+ARG HOME_DIR=/$USER_NAME
+ARG SOURCE_DIR=$HOME_DIR/source
+ARG APP_PATH=/var/vireo
ARG NODE_ENV=production
# Maven stage.
-FROM maven:3-openjdk-11-slim as maven
+FROM maven:3-eclipse-temurin-11-alpine as maven
ARG USER_ID
ARG USER_NAME
+ARG HOME_DIR
ARG SOURCE_DIR
+ARG APP_PATH
ARG NODE_ENV
ENV NODE_ENV=$NODE_ENV
-# Create the user and group (use a high ID to attempt to avoid conflicts).
-RUN groupadd --non-unique -g $USER_ID $USER_NAME && \
- useradd --non-unique -d /$USER_NAME -m -u $USER_ID -g $USER_ID $USER_NAME
+# Create the group (use a high ID to attempt to avoid conflits).
+RUN addgroup -g $USER_ID $USER_NAME
-# Install stable Nodejs and npm.
-RUN apt-get update && \
- apt-get upgrade -y && \
- apt-get install -y nodejs npm iproute2 && \
- apt-get clean && \
- rm -rf /var/lib/apt/lists/* && \
- npm cache clean -f && \
- npm install -g n && \
- n stable
+# Create the user (use a high ID to attempt to avoid conflits).
+RUN adduser -h $HOME_DIR -u $USER_ID -G $USER_NAME -D $USER_NAME
# Ensure source directory exists and has appropriate file permissions.
RUN mkdir -p $SOURCE_DIR && \
chown $USER_ID:$USER_ID $SOURCE_DIR && \
chmod g+s $SOURCE_DIR
+# Upgrade the system and install dependencies.
+RUN apk -U upgrade && \
+ apk add --update --no-cache nodejs npm make g++ py3-pip
+
# Set deployment directory.
WORKDIR $SOURCE_DIR
@@ -43,7 +43,7 @@ COPY ./src ./src
COPY ./build ./build
COPY ./package.json ./package.json
-# Assign file permissions.
+# Change ownership of source directory.
RUN chown -R $USER_ID:$USER_ID $SOURCE_DIR
# Login as user.
@@ -52,48 +52,47 @@ USER $USER_NAME
# Build.
RUN mvn package -Pproduction
-# Switch to Normal JRE Stage.
-FROM openjdk:11-jre-slim
+# JRE Stage.
+FROM eclipse-temurin:11-alpine
ARG USER_ID
ARG USER_NAME
+ARG HOME_DIR
ARG SOURCE_DIR
+ARG APP_PATH
+
+ENV APP_PATH=$APP_PATH
+
+# Create the group (use a high ID to attempt to avoid conflits).
+RUN addgroup -g $USER_ID $USER_NAME
-RUN apt-get update && \
- apt-get upgrade -y && \
- apt-get -y install gettext-base && \
- apt-get clean && \
- rm -rf /var/lib/apt/lists/*
+# Create the user (use a high ID to attempt to avoid conflits).
+RUN adduser -h $HOME_DIR -u $USER_ID -G $USER_NAME -D $USER_NAME
+
+# Ensure app path directory exists and has appropriate file permissions.
+RUN mkdir -p $APP_PATH && \
+ chown $USER_ID:$USER_ID $APP_PATH && \
+ chmod g+s $APP_PATH
+
+# Update the system and install gettext for envsubst.
+RUN apk -U upgrade && \
+ apk add --update --no-cache gettext
# Copy files from outside docker to inside.
COPY build/appConfig.js.template /usr/local/app/templates/appConfig.js.template
COPY build/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
-
-# Enable execute of docker entrypoint for root user.
-RUN chmod ugo+r /usr/local/app/templates/appConfig.js.template && \
- chmod ugo+rx /usr/local/bin/docker-entrypoint.sh
-
-# Create the user and group (use a high ID to attempt to avoid conflicts).
-RUN groupadd --non-unique -g $USER_ID $USER_NAME && \
- useradd --non-unique -d /$USER_NAME -m -u $USER_ID -g $USER_ID $USER_NAME
+RUN chmod ugo+x /usr/local/bin/docker-entrypoint.sh
# Login as user.
USER $USER_NAME
# Set deployment directory.
-WORKDIR /$USER_NAME
+WORKDIR $HOME_DIR
+
# Copy over the built artifact and library from the maven image.
COPY --from=maven $SOURCE_DIR/target/vireo-*.war ./vireo.war
COPY --from=maven $SOURCE_DIR/target/libs ./libs
-ENV AUTH_STRATEGY=weaverAuth
-
-ENV STOMP_DEBUG=false
-
-ENV AUTH_SERVICE_URL=http://localhost:9001/auth
-
-ENV APP_CONFIG_PATH=file:/$USER_NAME/appConfig.js
-
EXPOSE 9000
# Entrypoint performs environment substitution on appConfig.js.
diff --git a/README.md b/README.md
index 90497e80d7..1bde1045d6 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,7 @@ $ mvn clean spring-boot:run -Dproduction
$ mvn clean package -DskipTests -Dproduction -Dassets.uri=file:/opt/vireo/ -Dconfig.uri=file:/opt/vireo/config/
```
-If build succeeds, you should have both a `vireo-4.1.5.war` and a `vireo-4.1.5-install.zip` in the `target/` directory. When building for production required static assets are copied into the packaged war file and the index.html template is optimized for production. For development a symlink is used to allow the application to access required static assets.
+If build succeeds, you should have both a `vireo-4.2.0.war` and a `vireo-4.2.0-install.zip` in the `target/` directory. When building for production required static assets are copied into the packaged war file and the index.html template is optimized for production. For development a symlink is used to allow the application to access required static assets.
#### Apache Reverse Proxy Config
@@ -117,7 +117,7 @@ Unzip package into preferred directory (or any directory you choose):
```bash
$ cd /opt/vireo
-$ unzip vireo-4.1.5-install.zip
+$ unzip vireo-4.2.0-install.zip
```
### Directory Structure of installed package
@@ -190,13 +190,13 @@ ln -s /opt/vireo/webapp /opt/tomcat/webapps/ROOT
Copy war file into Tomcat webapps directory (your location may vary -- this is an example):
```bash
-$ cp ~/vireo-4.1.5.war /usr/local/tomcat/webapps/vireo.war
+$ cp ~/vireo-4.2.0.war /usr/local/tomcat/webapps/vireo.war
```
or as root:
```bash
-$ cp ~/vireo-4.1.5.war /usr/local/tomcat/webapps/ROOT.war
+$ cp ~/vireo-4.2.0.war /usr/local/tomcat/webapps/ROOT.war
```
**if not specifying assets.uri during build the assets will be stored under the vireo webapp's classpath, /opt/tomcat/webapps/vireo/WEB-INF/classes**
@@ -209,7 +209,7 @@ $ cp ~/vireo-4.1.5.war /usr/local/tomcat/webapps/ROOT.war
## Running WAR as a stand-alone Spring Boot application
```bash
-java -jar target/vireo-4.1.5.war
+java -jar target/vireo-4.2.0.war
```
diff --git a/build/appConfig.js.template b/build/appConfig.js.template
index 4ac6aaf4d1..85f2f54c5d 100644
--- a/build/appConfig.js.template
+++ b/build/appConfig.js.template
@@ -1,6 +1,6 @@
var appConfig = {
- 'version': '4.1.5',
+ 'version': '4.2.0',
'allowAnonymous': true,
'anonymousRole': 'ROLE_ANONYMOUS',
diff --git a/docker-compose.yml b/docker-compose.yml
index 4186412126..944f28d76f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,37 +1,50 @@
-version: '3.7'
+version: '3.8'
+
+networks:
+ net:
-networks:
- weaver:
-
-services:
-
- vireo_db:
- container_name: vireo_db
- hostname: vireo_db
- image: postgres
- environment:
- POSTGRES_DB: vireo
- POSTGRES_USER: vireo
- POSTGRES_PASSWORD: vireo
- volumes:
- - ${LOCAL_VIREO_PGDATA}:/var/lib/postgresql/data
- networks:
- weaver:
-
- vireo:
+volumes:
+ pgdata:
+ assets:
+
+services:
+
+ db:
+ image: postgres
+ environment:
+ - POSTGRES_DB=vireo
+ - POSTGRES_USER=vireo
+ - POSTGRES_PASSWORD=vireo
+ volumes:
+ - pgdata:/var/lib/postgresql/data
+ ports:
+ - 5432:5432
+ networks:
+ - net
+
+ adminer:
+ image: adminer
+ ports:
+ - 8080:8080
+ depends_on:
+ - db
+ networks:
+ - net
+
+ vireo:
container_name: vireo
- hostname: vireo
- build:
- dockerfile: Dockerfile
- context: './'
- image: ${IMAGE_HOST}/${SERVICE_PROJECT}${SERVICE_PATH}:${IMAGE_VERSION}
- ports:
- - 9000:9000
- env_file:
- - .env
- volumes:
- - ${LOCAL_VIREO_PATH}:${APP_PATH}
- depends_on:
- - vireo_db
- networks:
- weaver:
+ hostname: vireo
+ image: ${IMAGE_HOST}/${SERVICE_PROJECT}/${SERVICE_PATH}:${IMAGE_VERSION}
+ build:
+ dockerfile: Dockerfile
+ context: './'
+ env_file:
+ - .env
+ volumes:
+ - assets:${APP_PATH}
+ ports:
+ - 9000:9000
+ depends_on:
+ - db
+ networks:
+ - net
diff --git a/example.env b/example.env
index 961b1f0ad8..65d5deb8a9 100644
--- a/example.env
+++ b/example.env
@@ -4,32 +4,35 @@
##############################
IMAGE_HOST=127.0.0.1
+IMAGE_VERSION=4.2.0
+SERVICE_PROJECT=tdl
+SERVICE_PATH=vireo
-IMAGE_VERSION=0.0.1
+NODE_ENV=production
-SERVICE_PROJECT=vireo
+STOMP_DEBUG=false
-# Must have leading slash.
-SERVICE_PATH=/vireo
+AUTH_STRATEGY=weaverAuth
-LOCAL_VIREO_PATH=/var/vireo/
-LOCAL_VIREO_PGDATA=/var/vireo/pgdata/
+AUTH_SERVICE_URL="window.location.protocol + '//' + window.location.host + window.location.base + '/mock/auth'"
-NODE_ENV=production
-STOMP_DEBUG=false
+#SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.H2Dialect
+SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.PostgreSQLDialect
-AUTH_SERVICE_URL="window.location.protocol + '//' + window.location.host + window.location.base + '/mock/auth'"
+SPRING_JPA_HIBERNATE_DDL_AUTO=update
+
+#SPRING_DATASOURCE_URL=jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+#SPRING_DATASOURCE_DRIVERCLASSNAME=org.h2.Driver
+SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/vireo
+SPRING_DATASOURCE_DRIVERCLASSNAME=org.postgresql.Driver
-SPRING_DATASOURCE_URL=jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
-SPRING_DATASOURCE_DRIVERCLASSNAME=org.h2.Driver
SPRING_DATASOURCE_USERNAME=vireo
SPRING_DATASOURCE_PASSWORD=vireo
-SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.H2Dialect
-SPRING_JPA_HIBERNATE_DDL_AUTO=update
+
# location to place templated appConfig.js
-APP_PATH=/var/vireo/
+APP_PATH=/var/vireo
# must match directory of APP_PATH
APP_CONFIG_URI=file:/var/vireo/appConfig.js
APP_ASSETS_URI=file:/var/vireo/
diff --git a/package.json b/package.json
index 447b647e19..e20b548fb1 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "vireo",
"private": false,
- "version": "4.1.5",
+ "version": "4.2.0",
"description": "Vireo 4",
"homepage": "https://github.com/TexasDigitalLibrary/Vireo",
"repository": {
@@ -22,7 +22,7 @@
"build": "wvr build --clean"
},
"dependencies": {
- "@wvr/core": "2.2.4",
+ "@wvr/core": "2.2.5-rc.3",
"angular-ui-tinymce": "0.0.19",
"file-saver": "2.0.5",
"ng-csv": "0.3.6",
diff --git a/pom.xml b/pom.xml
index eb434539fb..d578a2afbe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
org.tdl
vireo
- 4.1.5
+ 4.2.0
Vireo
Vireo Thesis and Dissertation Submission System
diff --git a/src/main/java/org/tdl/vireo/config/VireoDatabaseConfig.java b/src/main/java/org/tdl/vireo/config/VireoDatabaseConfig.java
new file mode 100644
index 0000000000..7cc81d9876
--- /dev/null
+++ b/src/main/java/org/tdl/vireo/config/VireoDatabaseConfig.java
@@ -0,0 +1,20 @@
+package org.tdl.vireo.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class VireoDatabaseConfig {
+
+ @Value("${spring.sql.init.platform:''}")
+ private String platform;
+
+ public String getPlatform() {
+ return platform;
+ }
+
+ public void setPlatform(String platform) {
+ this.platform = platform;
+ }
+
+}
diff --git a/src/main/java/org/tdl/vireo/config/constant/ConfigurationName.java b/src/main/java/org/tdl/vireo/config/constant/ConfigurationName.java
index a7cd0535f1..c8381620f5 100644
--- a/src/main/java/org/tdl/vireo/config/constant/ConfigurationName.java
+++ b/src/main/java/org/tdl/vireo/config/constant/ConfigurationName.java
@@ -49,12 +49,21 @@ public class ConfigurationName {
// Theme settings
/** Background main color */
public final static String THEME_PATH = "theme_path";
+
+ /** Text main color */
+ public final static String TEXT_MAIN_COLOR = "text_main_color";
/** Background main color */
public final static String BACKGROUND_MAIN_COLOR = "background_main_color";
/** Background highlight color */
public final static String BACKGROUND_HIGHLIGHT_COLOR = "background_highlight_color";
+
+ /** Background text color */
+ public final static String BACKGROUND_HEADER_TEXT_COLOR = "background_header_text_color";
+
+ /** Background text color */
+ public final static String BACKGROUNT_FOOTER_TEXT_COLOR = "background_footer_text_color";
/** Submission Step Button main color when in "on" state */
public final static String BUTTON_MAIN_COLOR_ON = "button_main_color_on";
@@ -62,12 +71,30 @@ public class ConfigurationName {
/** Submission Step Button highlight color when in "on" state */
public final static String BUTTON_HIGHLIGHT_COLOR_ON = "button_highlight_color_on";
+ /** Submission Step Button text color when in "on" state */
+ public final static String BUTTON_TEXT_COLOR_ON = "button_text_color_on";
+
/** Submission Step Button main color when in "off" state */
public final static String BUTTON_MAIN_COLOR_OFF = "button_main_color_off";
/** Submission Step Button highlight color when in "off" state */
public final static String BUTTON_HIGHLIGHT_COLOR_OFF = "button_highlight_color_off";
+ /** Submission Step Button text color when in "off" state */
+ public final static String BUTTON_TEXT_COLOR_OFF = "button_text_color_off";
+
+ /** Admin main navigation tab background color */
+ public final static String ADMIN_TAB_MAIN_COLOR = "admin_tab_main_color";
+
+ /** Admin selected navigation tab background color */
+ public final static String ADMIN_TAB_SELECTED_COLOR = "admin_tab_selected_color";
+
+ /** Admin navigation tab text color*/
+ public final static String ADMIN_TAB_MAIN_TEXT_COLOR = "admin_tab_main_text_color";
+
+ /** Admin navigation tab text color*/
+ public final static String ADMIN_TAB_SELECTED_TEXT_COLOR = "admin_tab_selected_text_color";
+
/** Custom CSS */
public final static String LEFT_LOGO = "left_logo";
diff --git a/src/main/java/org/tdl/vireo/controller/SubmissionController.java b/src/main/java/org/tdl/vireo/controller/SubmissionController.java
index eade201171..3006f822bb 100644
--- a/src/main/java/org/tdl/vireo/controller/SubmissionController.java
+++ b/src/main/java/org/tdl/vireo/controller/SubmissionController.java
@@ -213,7 +213,11 @@ public ApiResponse getActionLogs(@WeaverUser User user, @PathVariable Long submi
@JsonView(Views.SubmissionIndividual.class)
@RequestMapping("/advisor-review/{advisorAccessHash}")
public ApiResponse getOne(@PathVariable String advisorAccessHash) {
- return new ApiResponse(SUCCESS, submissionRepo.findOneByAdvisorAccessHash(advisorAccessHash));
+ Submission submission = submissionRepo.findOneByAdvisorAccessHash(advisorAccessHash);
+
+ return submission.getSubmissionStatus().isEditableByReviewer()
+ ? new ApiResponse(SUCCESS, submission)
+ : new ApiResponse(ERROR, "Submission is not editable by reviewer");
}
@GetMapping("/advisor-review/{advisorAccessHash}/action-logs")
diff --git a/src/main/java/org/tdl/vireo/controller/SubmissionListController.java b/src/main/java/org/tdl/vireo/controller/SubmissionListController.java
index 83f9440e35..2079440521 100644
--- a/src/main/java/org/tdl/vireo/controller/SubmissionListController.java
+++ b/src/main/java/org/tdl/vireo/controller/SubmissionListController.java
@@ -4,10 +4,11 @@
import static edu.tamu.weaver.response.ApiStatus.SUCCESS;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
-import java.util.ArrayList;
+import edu.tamu.weaver.auth.annotation.WeaverUser;
+import edu.tamu.weaver.response.ApiResponse;
+import edu.tamu.weaver.validation.aspect.annotation.WeaverValidatedModel;
import java.util.List;
import java.util.Map;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,10 +34,6 @@
import org.tdl.vireo.model.repo.UserRepo;
import org.tdl.vireo.service.DefaultSubmissionListColumnService;
-import edu.tamu.weaver.auth.annotation.WeaverUser;
-import edu.tamu.weaver.response.ApiResponse;
-import edu.tamu.weaver.validation.aspect.annotation.WeaverValidatedModel;
-
@RestController
@RequestMapping("/submission-list")
public class SubmissionListController {
@@ -182,9 +179,14 @@ public ApiResponse getSavedFilters(@WeaverUser User user) {
@PreAuthorize("hasRole('REVIEWER')")
@RequestMapping(value = "/remove-saved-filter", method = POST)
public ApiResponse removeSavedFilter(@WeaverUser User user, @WeaverValidatedModel NamedSearchFilterGroup savedFilter) {
- user.getSavedFilters().remove(savedFilter);
- user = userRepo.save(user);
- return new ApiResponse(SUCCESS, user.getActiveFilter());
+ if (user.getSavedFilters().contains(savedFilter)) {
+ user.getSavedFilters().remove(savedFilter);
+ user = userRepo.save(user);
+
+ return new ApiResponse(SUCCESS, user.getActiveFilter());
+ }
+
+ return new ApiResponse(ERROR, "Cannot not find filter.");
}
@PreAuthorize("hasRole('REVIEWER')")
@@ -281,14 +283,10 @@ public ApiResponse clearFilterCriteria(@WeaverUser User user) {
@RequestMapping("/all-saved-filter-criteria")
@PreAuthorize("hasRole('REVIEWER')")
public ApiResponse getAllSaveFilterCriteria(@WeaverUser User user) {
- List userSavedFilters = user.getSavedFilters();
- List publicSavedFilters = namedSearchFilterGroupRepo.findByPublicFlagTrue();
-
- List allSavedFilters = new ArrayList();
- allSavedFilters.addAll(userSavedFilters);
- allSavedFilters.addAll(publicSavedFilters);
+ List all = namedSearchFilterGroupRepo.findByUserIsNotAndPublicFlagTrue(user);
+ all.addAll(user.getSavedFilters());
- return new ApiResponse(SUCCESS, userSavedFilters);
+ return new ApiResponse(SUCCESS, all);
}
@RequestMapping(value = "/save-filter-criteria", method = POST)
diff --git a/src/main/java/org/tdl/vireo/model/NamedSearchFilterGroup.java b/src/main/java/org/tdl/vireo/model/NamedSearchFilterGroup.java
index e273c41f7c..3a4fb8a959 100644
--- a/src/main/java/org/tdl/vireo/model/NamedSearchFilterGroup.java
+++ b/src/main/java/org/tdl/vireo/model/NamedSearchFilterGroup.java
@@ -3,6 +3,7 @@
import static javax.persistence.CascadeType.MERGE;
import static javax.persistence.CascadeType.REFRESH;
import static javax.persistence.FetchType.EAGER;
+import static javax.persistence.FetchType.LAZY;
import java.util.ArrayList;
import java.util.HashSet;
@@ -63,7 +64,7 @@ public class NamedSearchFilterGroup extends ValidatingBaseEntity {
private Sort sortDirection;
@OrderColumn
- @ManyToMany(cascade = { REFRESH, MERGE }, fetch = EAGER)
+ @ManyToMany(cascade = { REFRESH, MERGE }, fetch = LAZY)
private List savedColumns;
@Fetch(FetchMode.SELECT)
diff --git a/src/main/java/org/tdl/vireo/model/User.java b/src/main/java/org/tdl/vireo/model/User.java
index e84aadb92c..ff1ded4b8f 100644
--- a/src/main/java/org/tdl/vireo/model/User.java
+++ b/src/main/java/org/tdl/vireo/model/User.java
@@ -5,6 +5,7 @@
import static javax.persistence.CascadeType.REFRESH;
import static javax.persistence.CascadeType.REMOVE;
import static javax.persistence.FetchType.EAGER;
+import static javax.persistence.FetchType.LAZY;
import java.util.ArrayList;
import java.util.Collection;
@@ -121,7 +122,7 @@ public class User extends AbstractWeaverUserDetails {
@ManyToMany(cascade = { REFRESH }, fetch = EAGER)
private List filterColumns;
- @ManyToOne(cascade = { REFRESH, MERGE }, fetch = EAGER, optional = true)
+ @ManyToOne(cascade = { REFRESH, MERGE }, fetch = LAZY, optional = true)
private NamedSearchFilterGroup activeFilter;
@Fetch(FetchMode.SELECT)
diff --git a/src/main/java/org/tdl/vireo/model/repo/NamedSearchFilterGroupRepo.java b/src/main/java/org/tdl/vireo/model/repo/NamedSearchFilterGroupRepo.java
index 544ec59792..0708fea185 100644
--- a/src/main/java/org/tdl/vireo/model/repo/NamedSearchFilterGroupRepo.java
+++ b/src/main/java/org/tdl/vireo/model/repo/NamedSearchFilterGroupRepo.java
@@ -3,13 +3,14 @@
import java.util.List;
import org.tdl.vireo.model.NamedSearchFilterGroup;
+import org.tdl.vireo.model.User;
import org.tdl.vireo.model.repo.custom.NamedSearchFilterGroupRepoCustom;
import edu.tamu.weaver.data.model.repo.WeaverRepo;
public interface NamedSearchFilterGroupRepo extends WeaverRepo, NamedSearchFilterGroupRepoCustom {
- public List findByPublicFlagTrue();
+ public List findByUserIsNotAndPublicFlagTrue(User user);
public NamedSearchFilterGroup findByNameAndPublicFlagTrue(String name);
diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java
index 2642013089..510f088abc 100644
--- a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java
+++ b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java
@@ -36,6 +36,7 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.annotation.Transactional;
+import org.tdl.vireo.config.VireoDatabaseConfig;
import org.tdl.vireo.exception.OrganizationDoesNotAcceptSubmissionsException;
import org.tdl.vireo.model.Configuration;
import org.tdl.vireo.model.CustomActionDefinition;
@@ -100,6 +101,9 @@ public class SubmissionRepoImpl extends AbstractWeaverRepoImpl batchDynamicSubmissionQuery(NamedSearchFilterGroup activ
@Override
public Page pageableDynamicSubmissionQuery(NamedSearchFilterGroup activeFilter, List submissionListColums, Pageable pageable) throws ExecutionException {
+ long startTime = System.nanoTime();
+
QueryStrings queryBuilder = craftDynamicSubmissionQuery(activeFilter, submissionListColums, pageable);
Long total = jdbcTemplate.queryForObject(queryBuilder.getCountQuery(), Long.class);
- List ids = jdbcTemplate.query(queryBuilder.getQuery(), new RowMapper() {
+ logger.debug("Count query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds");
+ startTime = System.nanoTime();
+
+ List ids = jdbcTemplate.query(queryBuilder.getQuery(), new RowMapper<>() {
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getLong("ID");
}
});
- List submissions = new ArrayList();
+ logger.debug("ID query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds");
+ startTime = System.nanoTime();
+
+ List submissions = new ArrayList<>();
List unordered = submissionRepo.findAllById(ids);
+ logger.debug("Find all query for dynamic query took " + ((System.nanoTime() - startTime) / 1000000000.0) + " seconds");
+
// order them
for (Long id : ids) {
for (Submission sub : unordered) {
@@ -370,7 +384,7 @@ public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
private QueryStrings craftDynamicSubmissionQuery(NamedSearchFilterGroup activeFilter, List submissionListColums, Pageable pageable) {
// set up storage for user's preferred columns
- Set allColumnSearchFilters = new HashSet();
+ Set allColumnSearchFilters = new HashSet<>();
// get all the possible columns, some of which we will make visible
List allSubmissionListColumns = submissionListColumnRepo.findAll();
@@ -406,7 +420,7 @@ private QueryStrings craftDynamicSubmissionQuery(NamedSearchFilterGroup activeFi
}
// sort all submission list columns by sort order provided by users submission list columns
- Collections.sort(allSubmissionListColumns, new Comparator() {
+ Collections.sort(allSubmissionListColumns, new Comparator<>() {
@Override
public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
return svc1.getSortOrder().compareTo(svc2.getSortOrder());
@@ -415,17 +429,20 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
StringBuilder sqlSelectBuilder = new StringBuilder("SELECT DISTINCT s.id,");
- StringBuilder sqlCountSelectBuilder = new StringBuilder("SELECT COUNT(DISTINCT s.id) FROM submission s ");
+ StringBuilder sqlCountSelectBuilder = new StringBuilder();
- Map> sqlColumnsBuilders = new HashMap>();
+ Map> sqlColumnsBuilders = new HashMap<>();
+ Map> sqlCountWhereFilterBuilders = new HashMap<>();
+ Map sqlCountWherePredicate = new HashMap<>();
StringBuilder sqlJoinsBuilder = new StringBuilder();
- StringBuilder sqlWhereBuilder;
+ StringBuilder sqlBuilder;
+ StringBuilder sqlCountBuilder;
StringBuilder sqlWheresExcludeBuilder = new StringBuilder();
StringBuilder sqlOrderBysBuilder = new StringBuilder();
ArrayList sqlWhereBuilderList;
- ArrayList sqlAllColumnsWhereBuilderList = new ArrayList();
+ ArrayList sqlAllColumnsWhereBuilderList = new ArrayList<>();
int n = 0;
@@ -444,120 +461,165 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
Long predicateId = fieldPredicateRepo.findByValue(submissionListColumn.getPredicate()).getId();
// @formatter:off
- sqlJoinsBuilder.append("\nLEFT JOIN")
- .append("\n (SELECT sfv").append(n).append(".submission_id, fv").append(n).append(".*")
- .append("\n FROM submission_field_values sfv").append(n)
- .append("\n LEFT JOIN field_value fv").append(n).append(" ON fv").append(n).append(".id=sfv").append(n).append(".field_values_id ")
- .append("\n WHERE fv").append(n).append(".field_predicate_id=").append(predicateId).append(") pfv").append(n)
- .append("\n ON pfv").append(n).append(".submission_id=s.id");
- // @formatter:on
+ if (submissionListColumn.getSortOrder() > 0 || submissionListColumn.getFilters().size() > 0) {
+ sqlJoinsBuilder
+ .append("\nLEFT JOIN")
+ .append("\n (SELECT sfv").append(n).append(".submission_id, fv").append(n).append(".*")
+ .append("\n FROM submission_field_values sfv").append(n)
+ .append("\n LEFT JOIN field_value fv").append(n)
+ .append(" ON fv").append(n).append(".id=sfv").append(n).append(".field_values_id ")
+ .append("\n WHERE fv").append(n).append(".field_predicate_id=").append(predicateId).append(") pfv").append(n)
+ .append("\n ON pfv").append(n).append(".submission_id=s.id");
+ }
if (submissionListColumn.getSortOrder() > 0) {
- setColumnOrdering(submissionListColumn.getSort(), sqlSelectBuilder, sqlOrderBysBuilder, " pfv" + n + ".value");
+ if (submissionListColumn.getInputType().getName().equals("INPUT_DEGREEDATE")) {
+ setColumnOrderingForMonthYearDateFormat(submissionListColumn.getSort(), sqlSelectBuilder, sqlOrderBysBuilder, " pfv" + n);
+ } else {
+ setColumnOrdering(submissionListColumn.getSort(), sqlSelectBuilder, sqlOrderBysBuilder, " pfv" + n + ".value");
+ }
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilder = new StringBuilder();
+ sqlBuilder = new StringBuilder();
+ sqlCountBuilder = new StringBuilder();
switch (submissionListColumn.getInputType().getName()) {
case "INPUT_DEGREEDATE":
// Column's values are of type 'MMMM yyyy' (in SQL date format would be 'Month YYYY').
- sqlWhereBuilder.append("LOWER(pfv").append(n).append(".value) = LOWER('").append(filterString).append("')");
+ sqlBuilder.append("LOWER(pfv").append(n).append(".value) = LOWER('").append(filterString).append("')");
+ sqlCountBuilder.append("LOWER(fv.value) = LOWER('").append(filterString).append("')");
break;
case "INPUT_DATE":
// Column's values are of type 'yyyy-mm-dd' as required by the SQL standard to represent a date without time.
if (filterString.contains("|")) {
// Date Range
String[] dates = filterString.split(Pattern.quote("|"));
- sqlWhereBuilder
+ sqlBuilder
.append("CAST(pfv").append(n)
.append(".value AS DATE) BETWEEN CAST('").append(dates[0])
.append("' AS DATE) AND CAST('").append(dates[1])
.append("' AS DATE)");
+ sqlCountBuilder
+ .append("CAST(fv.value AS DATE) BETWEEN CAST('").append(dates[0])
+ .append("' AS DATE) AND CAST('").append(dates[1])
+ .append("' AS DATE)");
} else {
// Date Match
- sqlWhereBuilder.append("pfv").append(n).append(".value = '").append(filterString).append("'");
+ sqlBuilder.append("pfv").append(n).append(".value = '").append(filterString).append("'");
+ sqlCountBuilder.append("fv.value = '").append(filterString).append("'");
}
break;
case "INPUT_CHECKBOX":
- sqlWhereBuilder.append("pfv").append(n).append(".value = '").append(filterString).append("'");
+ sqlBuilder.append("pfv").append(n).append(".value = '").append(filterString).append("'");
+ sqlCountBuilder.append("fv.value = '").append(filterString).append("'");
// Column's values are a boolean
if (!Boolean.valueOf(filterString)) {
- sqlWhereBuilderList.add(sqlWhereBuilder);
- sqlWhereBuilder = new StringBuilder();
+ sqlWhereBuilderList.add(sqlBuilder);
- sqlWhereBuilder.append(" pfv").append(n).append(".value IS NULL");
+ if (!sqlCountWherePredicate.containsKey(predicateId)) {
+ sqlCountWherePredicate.put(predicateId, new StringBuilder());
+ }
+
+ sqlCountWherePredicate.get(predicateId).append(" (").append(sqlCountBuilder).append(") OR");
+
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append(" pfv").append(n).append(".value IS NULL");
+
+ sqlCountBuilder = new StringBuilder();
+ sqlCountBuilder.append(" fv.value IS NULL");
}
+
break;
default:
// Column's values can be handled by this default
if (submissionListColumn.getExactMatch()) {
// perform exact match
- sqlWhereBuilder.append("pfv").append(n).append(".value = '").append(filterString).append("'");
+ sqlBuilder.append("pfv").append(n).append(".value = '").append(filterString).append("'");
+ sqlCountBuilder.append("fv.value = '").append(filterString).append("'");
} else {
// perform like when input from text field
- sqlWhereBuilder.append("LOWER(pfv").append(n).append(".value) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlBuilder.append("LOWER(pfv").append(n).append(".value) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlCountBuilder.append("LOWER(fv.value) LIKE '%").append(filterString.toLowerCase()).append("%'");
}
+
break;
}
- if (sqlWhereBuilder.length() > 0) {
- sqlWhereBuilderList.add(sqlWhereBuilder);
+ if (sqlBuilder.length() > 0) {
+ sqlWhereBuilderList.add(sqlBuilder);
+
+ if (!sqlCountWherePredicate.containsKey(predicateId)) {
+ sqlCountWherePredicate.put(predicateId, new StringBuilder());
+ }
+
+ sqlCountWherePredicate.get(predicateId).append(" (").append(sqlCountBuilder).append(") OR");
}
}
- // all column search filter
- for (String filterString : allColumnSearchFilters) {
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("LOWER(pfv").append(n).append(".value) LIKE '%").append(filterString.toLowerCase()).append("%'");
- sqlAllColumnsWhereBuilderList.add(sqlWhereBuilder);
- }
+ if (submissionListColumn.getSortOrder() > 0 || submissionListColumn.getFilters().size() > 0) {
+ // all column search filter
+ for (String filterString : allColumnSearchFilters) {
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("LOWER(pfv").append(n).append(".value) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlAllColumnsWhereBuilderList.add(sqlBuilder);
+ }
- n++;
+ n++;
+ }
break;
case "id":
if (submissionListColumn.getSortOrder() > 0) {
- setColumnOrdering(submissionListColumn.getSort(), sqlSelectBuilder, sqlOrderBysBuilder, " s.id");
+ // The s.id is already on the submission, such just add it to the order by rather than call setColumnOrdering().
+ Sort sort = submissionListColumn.getSort();
+ if (sort == Sort.ASC || sort == Sort.DESC) {
+ sqlOrderBysBuilder.append(" s.id ").append(sort.name()).append(",");
+ }
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("s").append(".id = ").append(filterString);
- sqlWhereBuilderList.add(sqlWhereBuilder);
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("s").append(".id = ").append(filterString);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "id").add(sqlBuilder);
}
break;
case "submissionStatus.name":
+ sqlBuilder = new StringBuilder()
+ .append("\nLEFT JOIN submission_status ss ON ss.id=s.submission_status_id");
- sqlJoinsBuilder.append("\nLEFT JOIN submission_status ss ON ss.id=s.submission_status_id");
+ sqlJoinsBuilder.append(sqlBuilder);
+ sqlCountSelectBuilder.append(sqlBuilder);
if (submissionListColumn.getSortOrder() > 0) {
setColumnOrdering(submissionListColumn.getSort(), sqlSelectBuilder, sqlOrderBysBuilder, " ss.name");
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilder = new StringBuilder();
+ sqlBuilder = new StringBuilder();
if (submissionListColumn.getExactMatch()) {
- sqlWhereBuilder.append("ss").append(".name = '").append(filterString).append("'");
+ sqlBuilder.append("ss").append(".name = '").append(filterString).append("'");
} else {
// TODO: determine if status will ever be search using a like
- sqlWhereBuilder.append("LOWER(ss").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlBuilder.append("LOWER(ss").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
}
- sqlWhereBuilderList.add(sqlWhereBuilder);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "submissionStatus.name").add(sqlBuilder);
}
// all column search filter
for (String filterString : allColumnSearchFilters) {
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("LOWER(ss").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
- sqlAllColumnsWhereBuilderList.add(sqlWhereBuilder);
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("LOWER(ss").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlAllColumnsWhereBuilderList.add(sqlBuilder);
}
break;
@@ -565,7 +627,11 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
case "organization.name":
if (sqlJoinsBuilder.indexOf("LEFT JOIN organization o ON o.id=s.organization_id") == -1) {
- sqlJoinsBuilder.append("\nLEFT JOIN organization o ON o.id=s.organization_id");
+ sqlBuilder = new StringBuilder()
+ .append("\nLEFT JOIN organization o ON o.id=s.organization_id");
+
+ sqlJoinsBuilder.append(sqlBuilder);
+ sqlCountSelectBuilder.append(sqlBuilder);
}
if (submissionListColumn.getSortOrder() > 0) {
@@ -573,140 +639,154 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilder = new StringBuilder();
+ sqlBuilder = new StringBuilder();
if (submissionListColumn.getExactMatch()) {
- sqlWhereBuilder.append("o").append(".name = '").append(filterString).append("'");
+ sqlBuilder.append("o").append(".name = '").append(filterString).append("'");
} else {
// TODO: determine if organization name will ever be
// search using a like
- sqlWhereBuilder.append("LOWER(o").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlBuilder.append("LOWER(o").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
}
- sqlWhereBuilderList.add(sqlWhereBuilder);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "organization.name").add(sqlBuilder);
}
// all column search filter
for (String filterString : allColumnSearchFilters) {
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("LOWER(o").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
- sqlAllColumnsWhereBuilderList.add(sqlWhereBuilder);
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("LOWER(o").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlAllColumnsWhereBuilderList.add(sqlBuilder);
}
break;
case "organization.category.name":
+ sqlBuilder = new StringBuilder();
if (sqlJoinsBuilder.indexOf("LEFT JOIN organization o ON o.id=s.organization_id") == -1) {
- sqlJoinsBuilder.append("\nLEFT JOIN organization o ON o.id=s.organization_id");
+ sqlBuilder.append("\nLEFT JOIN organization o ON o.id=s.organization_id");
}
- sqlJoinsBuilder.append("\nLEFT JOIN organization_category oc ON oc.id=o.category_id");
+ sqlBuilder.append("\nLEFT JOIN organization_category oc ON oc.id=o.category_id");
+
+ sqlJoinsBuilder.append(sqlBuilder);
+ sqlCountSelectBuilder.append(sqlBuilder);
if (submissionListColumn.getSortOrder() > 0) {
setColumnOrdering(submissionListColumn.getSort(), sqlSelectBuilder, sqlOrderBysBuilder, " oc.name");
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilder = new StringBuilder();
+ sqlBuilder = new StringBuilder();
if (submissionListColumn.getExactMatch()) {
- sqlWhereBuilder.append("oc").append(".name = '").append(filterString).append("'");
+ sqlBuilder.append("oc").append(".name = '").append(filterString).append("'");
} else {
// TODO: determine if organization category name
// will ever be search using a like
- sqlWhereBuilder.append("LOWER(oc").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlBuilder.append("LOWER(oc").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
}
- sqlWhereBuilderList.add(sqlWhereBuilder);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "organization.category.name").add(sqlBuilder);
}
// all column search filter
for (String filterString : allColumnSearchFilters) {
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("LOWER(oc").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
- sqlAllColumnsWhereBuilderList.add(sqlWhereBuilder);
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("LOWER(oc").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlAllColumnsWhereBuilderList.add(sqlBuilder);
}
break;
case "assignee.email":
+ sqlBuilder = new StringBuilder()
+ .append("\nLEFT JOIN weaver_users a ON a.id=s.assignee_id");
- sqlJoinsBuilder.append("\nLEFT JOIN weaver_users a ON a.id=s.assignee_id");
+ sqlJoinsBuilder.append(sqlBuilder);
+ sqlCountSelectBuilder.append(sqlBuilder);
if (submissionListColumn.getSortOrder() > 0) {
setColumnOrdering(submissionListColumn.getSort(), sqlSelectBuilder, sqlOrderBysBuilder, " a.email");
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilder = new StringBuilder();
+ sqlBuilder = new StringBuilder();
if (filterString == null) {
- sqlWhereBuilder.append("a").append(".email IS NULL");
+ sqlBuilder.append("a").append(".email IS NULL");
} else if (submissionListColumn.getExactMatch()) {
- sqlWhereBuilder.append("a").append(".email = '").append(filterString).append("'");
+ sqlBuilder.append("a").append(".email = '").append(filterString).append("'");
} else {
- sqlWhereBuilder.append("LOWER(a").append(".email) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlBuilder.append("LOWER(a").append(".email) LIKE '%").append(filterString.toLowerCase()).append("%'");
}
- sqlWhereBuilderList.add(sqlWhereBuilder);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "assignee.email").add(sqlBuilder);
}
// all column search filter
for (String filterString : allColumnSearchFilters) {
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("LOWER(a").append(".email) LIKE '%").append(filterString.toLowerCase()).append("%'");
- sqlAllColumnsWhereBuilderList.add(sqlWhereBuilder);
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("LOWER(a").append(".email) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlAllColumnsWhereBuilderList.add(sqlBuilder);
}
break;
case "embargoTypes.name":
- // @formatter:off
- sqlJoinsBuilder.append("\nLEFT JOIN")
- .append("\n (SELECT e.id, e.name, semt.submission_id")
- .append("\n FROM embargo e")
- .append("\n LEFT JOIN submission_embargo_types semt")
- .append("\n ON semt.embargo_types_id=e.id) embs")
- .append("\n ON embs.submission_id=s.id");
- // @formatter:on
+ sqlBuilder = new StringBuilder()
+ .append("\nLEFT JOIN")
+ .append("\n (SELECT e.id, e.name, semt.submission_id")
+ .append("\n FROM embargo e")
+ .append("\n LEFT JOIN submission_embargo_types semt")
+ .append("\n ON semt.embargo_types_id=e.id) embs")
+ .append("\n ON embs.submission_id=s.id");
+
+ sqlJoinsBuilder.append(sqlBuilder);
+ sqlCountSelectBuilder.append(sqlBuilder);
if (submissionListColumn.getSortOrder() > 0) {
setColumnOrdering(submissionListColumn.getSort(), sqlSelectBuilder, sqlOrderBysBuilder, " embs.name");
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilder = new StringBuilder();
+ sqlBuilder = new StringBuilder();
- sqlWhereBuilder.append(" embs").append(".name = '").append(filterString).append("'");
+ sqlBuilder.append(" embs").append(".name = '").append(filterString).append("'");
if (filterString.equals("None")) {
- sqlWhereBuilderList.add(sqlWhereBuilder);
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("embs.id IS NULL");
+ sqlWhereBuilderList.add(sqlBuilder);
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("embs.id IS NULL");
}
- sqlWhereBuilderList.add(sqlWhereBuilder);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "embargoTypes.name").add(sqlBuilder);
}
// all column search filter
for (String filterString : allColumnSearchFilters) {
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("LOWER(embs").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
- sqlAllColumnsWhereBuilderList.add(sqlWhereBuilder);
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("LOWER(embs").append(".name) LIKE '%").append(filterString.toLowerCase()).append("%'");
+ sqlAllColumnsWhereBuilderList.add(sqlBuilder);
}
break;
case "lastEvent":
- // @formatter:off
-
- sqlJoinsBuilder.append("\nLEFT JOIN")
- .append("\n (SELECT al.id, al.action_date, al.entry, al.action_logs_id")
- .append("\n FROM action_log al")
- .append("\n WHERE (al.action_logs_id = id)")
- .append("\n ORDER BY al.action_date DESC")
- .append("\n LIMIT 1) als")
- .append("\n ON action_logs_id = s.submission_status_id");
- // @formatter:on
+ sqlBuilder = new StringBuilder()
+ .append("\nLEFT JOIN")
+ .append("\n (SELECT al.id, al.action_date, al.entry, al.action_logs_id")
+ .append("\n FROM action_log al")
+ .append("\n WHERE (al.action_logs_id = id)")
+ .append("\n ORDER BY al.action_date DESC")
+ .append("\n LIMIT 1) als")
+ .append("\n ON action_logs_id = s.submission_status_id");
+
+ sqlJoinsBuilder.append(sqlBuilder);
+ sqlCountSelectBuilder.append(sqlBuilder);
// TODO: finish sqlWheresBuilder.
@@ -735,7 +815,9 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilderList.add(buildSubmissionDateFieldString("submission_date", filterString));
+ sqlBuilder = buildSubmissionDateFieldString("submission_date", filterString);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "submissionDate").add(sqlBuilder);
}
break;
@@ -746,7 +828,9 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilderList.add(buildSubmissionDateFieldString("approve_application_date", filterString));
+ sqlBuilder = buildSubmissionDateFieldString("approve_application_date", filterString);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "approveApplicationDate").add(sqlBuilder);
}
break;
@@ -757,7 +841,9 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilderList.add(buildSubmissionDateFieldString("approve_advisor_date", filterString));
+ sqlBuilder = buildSubmissionDateFieldString("approve_advisor_date", filterString);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "approveAdvisorDate").add(sqlBuilder);
}
break;
@@ -768,25 +854,29 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
}
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilderList.add(buildSubmissionDateFieldString("approve_embargo_date", filterString));
+ sqlBuilder = buildSubmissionDateFieldString("approve_embargo_date", filterString);
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "approveEmbargoDate").add(sqlBuilder);
}
break;
case "customActionValues":
+ sqlBuilder = new StringBuilder()
+ .append("\nLEFT JOIN")
+ .append("\n (SELECT submission_id, value, label")
+ .append("\n FROM submission_custom_action_values scav")
+ .append("\n LEFT JOIN custom_action_value cav ON scav.custom_action_values_id = cav .id")
+ .append("\n LEFT JOIN custom_action_definition cad ON cav.definition_id = cad.id) scavcavcad")
+ .append("\n ON scavcavcad.submission_id = s.id");
- // @formatter:off
- sqlJoinsBuilder.append("\nLEFT JOIN")
- .append("\n (SELECT submission_id, value, label")
- .append("\n FROM submission_custom_action_values scav")
- .append("\n LEFT JOIN custom_action_value cav ON scav.custom_action_values_id = cav .id")
- .append("\n LEFT JOIN custom_action_definition cad ON cav.definition_id = cad.id) scavcavcad")
- .append("\n ON scavcavcad.submission_id = s.id");
- // @formatter:on
+ sqlJoinsBuilder.append(sqlBuilder);
+ sqlCountSelectBuilder.append(sqlBuilder);
for (String filterString : submissionListColumn.getFilters()) {
- sqlWhereBuilder = new StringBuilder();
- sqlWhereBuilder.append("scavcavcad.value = true AND scavcavcad.label = '" + filterString + "'");
- sqlWhereBuilderList.add(sqlWhereBuilder);
+ sqlBuilder = new StringBuilder();
+ sqlBuilder.append("scavcavcad.value = true AND scavcavcad.label = '" + filterString + "'");
+ sqlWhereBuilderList.add(sqlBuilder);
+ getFromBuildersMap(sqlCountWhereFilterBuilders, "customActionValues").add(sqlBuilder);
}
break;
@@ -800,9 +890,10 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
}
}
- // complete select clause
+ // Complete the select clause.
sqlSelectBuilder.setLength(sqlSelectBuilder.length() - 1);
sqlSelectBuilder.append(" FROM submission s");
+ sqlCountSelectBuilder.insert(0, "SELECT COUNT(DISTINCT s.id) FROM submission s");
// if ordering, complete order by clause and strip the tailing comma
if (sqlOrderBysBuilder.length() > 0) {
@@ -811,46 +902,92 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
}
// build WHERE query such that OR is used for conditions inside a column and AND is used across each different column.
- sqlWhereBuilder = new StringBuilder();
+ sqlBuilder = new StringBuilder();
if (sqlColumnsBuilders.size() > 0 || sqlAllColumnsWhereBuilderList.size() > 0 || sqlWheresExcludeBuilder.length() > 0) {
- sqlWhereBuilder.append("\nWHERE ");
+ sqlBuilder.append("\nWHERE ");
for (Entry> list : sqlColumnsBuilders.entrySet()) {
- sqlWhereBuilder.append("(");
+ sqlBuilder.append("(");
for (StringBuilder builder : list.getValue()) {
- sqlWhereBuilder.append("(").append(builder).append(") OR ");
+ sqlBuilder.append("(").append(builder).append(") OR ");
}
- // remove last " OR".
- sqlWhereBuilder.setLength(sqlWhereBuilder.length() - 4);
+ // remove last " OR ".
+ sqlBuilder.setLength(sqlBuilder.length() - 4);
- sqlWhereBuilder.append(") AND ");
+ sqlBuilder.append(") AND ");
}
if (sqlAllColumnsWhereBuilderList.size() > 0) {
- sqlWhereBuilder.append("(");
+ sqlBuilder.append("(");
for (StringBuilder builder : sqlAllColumnsWhereBuilderList) {
- sqlWhereBuilder.append("(").append(builder).append(") OR ");
+ sqlBuilder.append("(").append(builder).append(") OR ");
}
- // remove last " OR".
- sqlWhereBuilder.setLength(sqlWhereBuilder.length() - 4);
+ // remove last " OR ".
+ sqlBuilder.setLength(sqlBuilder.length() - 4);
- sqlWhereBuilder.append(") AND ");
+ sqlBuilder.append(") AND ");
}
if (sqlWheresExcludeBuilder.length() > 0) {
- sqlWhereBuilder.append("(").append(sqlWheresExcludeBuilder).append(")");
+ sqlBuilder.append("(").append(sqlWheresExcludeBuilder).append(")");
+ } else {
+ // remove last " AND "
+ sqlBuilder.setLength(sqlBuilder.length() - 5);
+ }
+ }
+
+ if (sqlCountWherePredicate.size() > 0 || sqlCountWhereFilterBuilders.size() > 0 || sqlWheresExcludeBuilder.length() > 0) {
+
+ // Conditions are AND across different predicates but are OR within the same predicate.
+ if (sqlCountWherePredicate.size() > 0) {
+ sqlCountSelectBuilder.append("\nLEFT JOIN submission_field_values sfv ON s.id = sfv.submission_id");
+ sqlCountSelectBuilder.append("\nINNER JOIN field_value fv ON sfv.field_values_id = fv.id");
+ sqlCountSelectBuilder.append("\nWHERE");
+
+ sqlCountWherePredicate.forEach((id, filter) -> {
+ if (filter.length() > 0) {
+ // Remove the last " OR".
+ filter.setLength(filter.length() - 3);
+
+ sqlCountSelectBuilder
+ .append("\n(fv.field_predicate_id = ").append(id)
+ .append(" AND (").append(filter).append(")) AND");
+ }
+ });
+ } else {
+ sqlCountSelectBuilder.append("\nWHERE");
+ }
+
+ // Conditions are AND across different filters and OR within the same filter.
+ if (sqlCountWhereFilterBuilders.size() > 0) {
+ sqlCountWhereFilterBuilders.forEach((key, list) -> {
+ sqlCountSelectBuilder.append(" (");
+
+ list.forEach(filter -> {
+ sqlCountSelectBuilder.append(" (").append(filter).append(") OR");
+ });
+
+ // Remove the last " OR".
+ sqlCountSelectBuilder.setLength(sqlCountSelectBuilder.length() - 3);
+
+ sqlCountSelectBuilder.append(")\n AND");
+ });
+ }
+
+ if (sqlWheresExcludeBuilder.length() > 0) {
+ sqlCountSelectBuilder.append("\n(").append(sqlWheresExcludeBuilder).append(")");
} else {
// remove last " AND"
- sqlWhereBuilder.setLength(sqlWhereBuilder.length() - 5);
+ sqlCountSelectBuilder.setLength(sqlCountSelectBuilder.length() - 4);
}
}
- String sqlQuery = sqlSelectBuilder.toString() + sqlJoinsBuilder.toString() + sqlWhereBuilder.toString();
- String sqlCountQuery = sqlCountSelectBuilder.toString() + sqlJoinsBuilder.toString() + sqlWhereBuilder.toString();
+ String sqlQuery = sqlSelectBuilder.toString() + sqlJoinsBuilder.toString() + sqlBuilder.toString();
+ String sqlCountQuery = sqlCountSelectBuilder.toString();
if (pageable != null) {
// determine the offset and limit of the query
@@ -867,16 +1004,44 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) {
}
public void setColumnOrdering(Sort sort, StringBuilder sqlSelectBuilder, StringBuilder sqlOrderBysBuilder, String value) {
- sqlSelectBuilder.append(value).append(",");
- switch (sort) {
- case ASC:
- sqlOrderBysBuilder.append(value).append(" ASC,");
- break;
- case DESC:
- sqlOrderBysBuilder.append(value).append(" DESC,");
- break;
- default:
- break;
+ if (sort == Sort.ASC || sort == Sort.DESC) {
+ sqlSelectBuilder.append(value).append(",");
+ sqlOrderBysBuilder.append(value).append(" ").append(sort.name()).append(",");
+ }
+ }
+
+ /**
+ * Handle case where Postgresql requires help casting the date string to a date type.
+ *
+ * Some SQL engines can directly cast the "Month Year" format to a date but postgresql cannot.
+ * Because of this limitation in Postgresql, all dates cannot be cast.
+ * Instead, the "Month Year" formatted data must be handled as an exception case.
+ *
+ * This converts the "Month Year" format into a "Month Day, Year" format to make Postgresql happy.
+ * Then this converts that date resulting string into a date type for proper SQL sorting.
+ *
+ * This would also be easier to add the cast on the ORDER BY and not need to add a column in the select clause.
+ * However, another Postgresql problem prevents this from working when DISTINCT is in use.
+ * Postgresql will falsely claim that pfv0.value is not in the SELECT clause when it actually is while DISTINCT is present.
+ *
+ * @param sort The sort direction.
+ * @param sqlSelectBuilder The SQL select builder string.
+ * @param sqlOrderBysBuilder The SQL order by builder string.
+ * @param table The table to select from when joining on the assumption that the value is "table".value.
+ */
+ public void setColumnOrderingForMonthYearDateFormat(Sort sort, StringBuilder sqlSelectBuilder, StringBuilder sqlOrderBysBuilder, String table) {
+ if (sort == Sort.ASC || sort == Sort.DESC) {
+ sqlSelectBuilder.append(table).append(".value,");
+
+ if ("h2".equals(vireoDatabaseConfig.getPlatform())) {
+ sqlSelectBuilder.append(" PARSEDATETIME(").append(table).append(".value, 'MMM yyyy') AS");
+ } else {
+ sqlSelectBuilder.append(" CAST(REPLACE(").append(table).append(".value, ' ', ' 1, ') AS DATE) AS");
+ }
+
+ sqlSelectBuilder.append(table).append("_date,");
+
+ sqlOrderBysBuilder.append(table).append("_date ").append(sort.name()).append(",");
}
}
@@ -886,32 +1051,43 @@ protected String getChannel() {
}
/**
- * Build a date field string given some filter.
+ * Build a submission date field string given some filter.
*
- * @param id The ID to append to the field name alias.
+ * This is form submission date fields that are already stored in the SQL date format.
+ *
+ * @param column The column name to filter.
* @param filter The filter.
* @return A constructed string builder appropriately casting the date.
*/
- private StringBuilder buildDateFieldString(int id, String filter) {
+ private StringBuilder buildSubmissionDateFieldString(String column, String filter) {
+ if (filter.contains("|")) {
+ String[] dates = filter.split(Pattern.quote("|"));
return new StringBuilder()
- .append("pfv").append(id)
- .append(".value = CAST('").append(filter)
+ .append("s.").append(column)
+ .append(" BETWEEN CAST('").append(dates[0])
+ .append("' AS DATE) AND CAST('").append(dates[1])
+ .append("' AS DATE)");
+ }
+ return new StringBuilder()
+ .append("s.").append(column)
+ .append(" = CAST('").append(filter)
.append("' AS DATE)");
}
/**
- * Build a submission date field string given some filter.
+ * Get the builders list array for some key, initializing that key if not found.
*
- * This is form submission date fields that are already stored in the SQL date format.
+ * @param map The map of builders to select from.
+ * @param key The identifier.
*
- * @param column The column name to filter.
- * @param filter The filter.
- * @return A constructed string builder appropriately casting the date.
+ * @return An array of the builders for the given key.
*/
- private StringBuilder buildSubmissionDateFieldString(String column, String filter) {
- return new StringBuilder()
- .append("s.").append(column)
- .append(" = CAST('").append(filter).append("' AS DATE)");
+ private ArrayList getFromBuildersMap(Map> map, String key) {
+ if (!map.containsKey(key)) {
+ map.put(key, new ArrayList());
+ }
+
+ return map.get(key);
}
private class QueryStrings {
diff --git a/src/main/java/org/tdl/vireo/service/VireoThemeManagerService.java b/src/main/java/org/tdl/vireo/service/VireoThemeManagerService.java
index 9e4ce62afb..fb96bca5cc 100644
--- a/src/main/java/org/tdl/vireo/service/VireoThemeManagerService.java
+++ b/src/main/java/org/tdl/vireo/service/VireoThemeManagerService.java
@@ -24,7 +24,7 @@ public class VireoThemeManagerService extends SimpleThemeManagerService implemen
@Override
public Map getThemeProperties() {
- String[] themePropertyNames = { "background_main_color", "background_highlight_color", "button_main_color_on", "button_highlight_color_on", "button_main_color_off", "button_highlight_color_off" };
+ String[] themePropertyNames = {"text_main_color", "background_main_color", "background_highlight_color", "background_header_text_color", "background_footer_text_color", "button_main_color_on", "button_highlight_color_on", "button_text_color_on", "button_main_color_off", "button_highlight_color_off", "button_text_color_off", "admin_tab_main_color", "admin_tab_selected_color", "admin_tab_main_text_color", "admin_tab_selected_text_color"};
List themePropertyNamesList = Arrays.asList(themePropertyNames);
List themeConfigurations = configurationRepo.getAllByType("lookAndFeel");
HashMap themeProperties = new HashMap();
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 17a9341694..ee87b0aed8 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -114,7 +114,7 @@ info:
app:
- url: http://localhost:9000
+ url: http://localhost:${server.port}
# value generated from property assets.uri
# either defined in pom.xml or via package argument
@@ -142,7 +142,7 @@ app:
# edu.tamu.weaver.auth.service.CryptoService
secret: verysecretsecret
# edu.tamu.weaver.filter.CorsFilter
- allow-access: http://localhost:9000
+ allow-access: http://localhost:${server.port}
# edu.tamu.weaver.email.config.WeaverEmailConfig
email:
diff --git a/src/main/resources/organization/SYSTEM_Organization_Definition.json b/src/main/resources/organization/SYSTEM_Organization_Definition.json
index e02949061d..1e90a9ed21 100644
--- a/src/main/resources/organization/SYSTEM_Organization_Definition.json
+++ b/src/main/resources/organization/SYSTEM_Organization_Definition.json
@@ -18,7 +18,7 @@
"originalNotes":[
{
"name":"name",
- "text":"Your name should appear as it does on your title page. You can use Unicode characters, if your computer supports them",
+ "text":"Your name should appear as it does on your title page. You can use Unicode characters, if your computer supports them.",
"originatingWorkflowStep":{
"name":"Verify Personal Information"
}
diff --git a/src/main/resources/settings/SYSTEM_Defaults.json b/src/main/resources/settings/SYSTEM_Defaults.json
index f99f3754d3..bea6e82458 100644
--- a/src/main/resources/settings/SYSTEM_Defaults.json
+++ b/src/main/resources/settings/SYSTEM_Defaults.json
@@ -85,12 +85,21 @@
{
"theme_path":"configuration/theme/"
},
+ {
+ "text_main_color":"#757575"
+ },
{
"background_main_color":"#1b333f"
},
{
"background_highlight_color":"#43606e"
},
+ {
+ "background_header_text_color":"#ffffff"
+ },
+ {
+ "background_footer_text_color":"#ffffff"
+ },
{
"button_main_color_on":"#1b333f"
},
@@ -98,10 +107,28 @@
"button_highlight_color_on":"#43606e"
},
{
- "button_main_color_off":"#92aa9c"
+ "button_text_color_on":"#ffffff"
+ },
+ {
+ "button_main_color_off":"#424D46"
+ },
+ {
+ "button_highlight_color_off":"#697A70"
+ },
+ {
+ "button_text_color_off":"#ffffff"
+ },
+ {
+ "admin_tab_main_color":"#adbdaa"
+ },
+ {
+ "admin_tab_selected_color":"#ffffff"
+ },
+ {
+ "admin_tab_main_text_color":"#1D3541"
},
{
- "button_highlight_color_off":"#adbdaa"
+ "admin_tab_selected_text_color":"#1D3541"
},
{
"left_logo":"resources/images/default-left-logo.png"
diff --git a/src/main/webapp/app/controllers/settingsController.js b/src/main/webapp/app/controllers/settingsController.js
index ff903f361f..461356c4b8 100644
--- a/src/main/webapp/app/controllers/settingsController.js
+++ b/src/main/webapp/app/controllers/settingsController.js
@@ -251,7 +251,9 @@ vireo.controller("SettingsController", function ($controller, $injector, $scope,
};
$scope.resetConfiguration = function (type, name) {
- return $scope.settings.configurable[type][name].reset();
+ if ($scope.settings.configurable[type][name].id) {
+ return $scope.settings.configurable[type][name].reset();
+ }
};
$scope.saveDegree = function (degree) {
diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js
index cf1c8edf19..2d59366374 100644
--- a/src/main/webapp/app/controllers/submission/submissionListController.js
+++ b/src/main/webapp/app/controllers/submission/submissionListController.js
@@ -29,6 +29,8 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
$scope.fieldPredicates = FieldPredicateRepo.getAll();
+ var rowFilterTitle = "Exclude";
+
var ready = $q.all([SubmissionListColumnRepo.ready(), ManagerSubmissionListColumnRepo.ready(), EmailTemplateRepo.ready(), FieldPredicateRepo.ready()]);
var updateChange = function(change) {
@@ -51,25 +53,19 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
var start;
var query = function () {
+ var sessionPageNumber = sessionStorage.getItem("list-page-number");
+ var sessionPageSize = sessionStorage.getItem("list-page-size");
+
$scope.tableParams = new NgTableParams({
- page: $scope.page.number,
- count: $scope.page.count
+ page: angular.isDefined(sessionPageNumber) && sessionPageNumber !== null ? sessionPageNumber : $scope.page.number,
+ count: angular.isDefined(sessionPageSize) && sessionPageSize !== null ? sessionPageSize : $scope.page.count,
}, {
counts: $scope.page.options,
total: $scope.page.totalElements,
filterDelay: 0,
getData: function (params) {
start = window.performance.now();
- return SubmissionRepo.query($scope.userColumns, params.page() > 0 ? params.page() - 1 : params.page(), params.count()).then(function (response) {
- angular.extend($scope.page, angular.fromJson(response.body).payload.ApiPage);
- // NOTE: this causes way to many subscriptions!!!
- // SubmissionRepo.addAll($scope.page.content);
- params.total($scope.page.totalElements);
- $scope.page.count = params.count();
- sessionStorage.setItem("list-page-size", $scope.page.count);
- sessionStorage.setItem("list-page-number", $scope.page.number + 1);
- return $scope.page.content;
- });
+ return queryGetData(params);
}.bind(start)
});
$scope.finished = function() {
@@ -77,54 +73,114 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
}.bind(start);
}.bind(start);
- var update = function () {
+ var queryGetData = function (params, forcePageNumber) {
+ var requestPage = 0;
- SavedFilterRepo.reset();
+ if (forcePageNumber === undefined) {
+ if (params.page() > 0) {
+ requestPage = params.page() - 1;
+ }
+ } else {
+ requestPage = forcePageNumber;
+ }
- SubmissionListColumnRepo.reset();
+ return SubmissionRepo.query($scope.userColumns, requestPage, params.count()).then(function (response) {
+ var page = angular.fromJson(response.body).payload.ApiPage;
- ManagerSubmissionListColumnRepo.reset();
+ // Forcibly fix invalid page and try again, but only once.
+ if (forcePageNumber === undefined && angular.isDefined(page.number) && angular.isDefined(page.totalPages)) {
+ var totalPages = parseInt(page.totalPages);
+ var pageNumber = parseInt(page.number);
- $q.all([SavedFilterRepo.ready(), SubmissionListColumnRepo.ready(), ManagerSubmissionListColumnRepo.ready()]).then(function() {
- ManagerSubmissionListColumnRepo.submissionListPageSize().then(function(response) {
- var apiRes = angular.fromJson(response.body);
- if(apiRes.meta.status === 'SUCCESS') {
- $scope.page.count = sessionStorage.getItem("list-page-size") ? sessionStorage.getItem("list-page-size") : apiRes.payload.Integer;
+ if (!isNaN(totalPages) && !isNaN(pageNumber) && (pageNumber > totalPages || pageNumber < 0)) {
+ pageNumber = pageNumber > totalPages && totalPages > 0 ? totalPages - 1 : 0;
+
+ console.warn("Invalid page number '" + page.number + "' detected, forcibly resetting to page '" + pageNumber + "' and trying again.");
+
+ return queryGetData(params, pageNumber);
}
+ }
- var managerFilterColumns = ManagerFilterColumnRepo.getAll();
- var submissionListColumns = SubmissionListColumnRepo.getAll();
+ angular.extend($scope.page, page);
- $scope.userColumns = angular.fromJson(angular.toJson(ManagerSubmissionListColumnRepo.getAll()));
+ // The service sets page number starting at 0, which needs to be incremented by 1 when provided.
+ if (angular.isDefined(page.number)) {
+ $scope.page.number++;
+ }
+
+ params.total($scope.page.totalElements);
+ params.page($scope.page.number);
+ $scope.page.count = params.count();
+ sessionStorage.setItem("list-page-size", $scope.page.count);
+ sessionStorage.setItem("list-page-number", $scope.page.number);
- angular.forEach($scope.userColumns, function (userColumn) {
- if ($scope.activeFilters.sortColumnTitle === userColumn.title) {
- userColumn.sortOrder = 1;
- userColumn.sort = $scope.activeFilters.sortDirection;
+ return $scope.page.content;
+ });
+ }
+
+ var update = function (reloadList) {
+ SavedFilterRepo.reset();
+ ManagerFilterColumnRepo.reset();
+ SubmissionListColumnRepo.reset();
+ ManagerSubmissionListColumnRepo.reset();
+
+ $q.all([SavedFilterRepo.ready(), SubmissionListColumnRepo.ready(), ManagerSubmissionListColumnRepo.ready()]).then(function() {
+
+ // Only get the list size if/when there is no page count set.
+ if (angular.isUndefined(sessionStorage.getItem("list-page-size")) || sessionStorage.getItem("list-page-size") === null) {
+ ManagerSubmissionListColumnRepo.submissionListPageSize().then(function (response) {
+ var apiRes = angular.fromJson(response.body);
+
+ if (apiRes.meta.status === 'SUCCESS') {
+ $scope.page.count = apiRes.payload.Integer;
+ } else if (sessionStorage.getItem("list-page-size")) {
+ $scope.page.count = sessionStorage.getItem("list-page-size");
}
+
+ processUpdate(reloadList);
});
+ } else {
+ processUpdate(reloadList);
+ }
+ });
+ };
+
+ var processUpdate = function (reloadList) {
+ var managerFilterColumns = ManagerFilterColumnRepo.getAll().filter(function excludeSearchBox(slc) {
+ return slc.title !== 'Search Box';
+ });
+ var submissionListColumns = SubmissionListColumnRepo.getAll();
- $scope.excludedColumns = [];
+ $scope.userColumns = angular.fromJson(angular.toJson(ManagerSubmissionListColumnRepo.getAll()));
- angular.copy($scope.userColumns, $scope.excludedColumns);
+ angular.forEach($scope.userColumns, function (userColumn) {
+ if ($scope.activeFilters.sortColumnTitle === userColumn.title) {
+ userColumn.sortOrder = 1;
+ userColumn.sort = $scope.activeFilters.sortDirection;
+ }
+ });
- $scope.excludedColumns.push(SubmissionListColumnRepo.findByTitle('Search Box'));
+ $scope.excludedColumns = [];
- $scope.columns = angular.fromJson(angular.toJson($filter('orderBy')($filter('exclude')(submissionListColumns, $scope.excludedColumns, 'title'), 'title')));
+ angular.copy($scope.userColumns, $scope.excludedColumns);
- angular.extend(filterColumns, {
- userFilterColumns: managerFilterColumns,
- inactiveFilterColumns: $filter('orderBy')($filter('exclude')(submissionListColumns, managerFilterColumns, 'title'), 'title')
- });
+ $scope.excludedColumns.push(SubmissionListColumnRepo.findByTitle('Search Box'));
- query();
+ $scope.columns = angular.fromJson(angular.toJson($filter('orderBy')($filter('exclude')(submissionListColumns, $scope.excludedColumns, 'title'), 'title')));
- updateChange(false);
- });
+ angular.extend(filterColumns, {
+ userFilterColumns: managerFilterColumns,
+ inactiveFilterColumns: $filter('orderBy')($filter('exclude')(submissionListColumns, managerFilterColumns, 'title'), 'title')
});
+
+ if (reloadList === true) {
+ query();
+ }
+
+ updateChange(false);
};
- update();
+ update(true);
var assignableUsers = UserRepo.getAssignableUsers(0, 0);
var savedFilters = SavedFilterRepo.getAll();
@@ -210,10 +266,13 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
var addFilter = function (column, gloss) {
+ $scope.resetPagination();
+
var filterValue = $scope.furtherFilterBy[column.title.split(" ").join("")];
if (filterValue !== null) {
filterValue = filterValue.toString();
}
+
$scope.activeFilters.addFilter(column.title, filterValue, gloss, column.exactMatch).then(function () {
$scope.furtherFilterBy[column.title.split(" ").join("")] = "";
query();
@@ -265,12 +324,17 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
});
};
- $scope.addRowFilter = function ($index, row) {
+ $scope.addRowFilter = function ($index, row) {
+ // When removing the last row for a page, and the page number is the last
+ if ($scope.page.content.length == 1 && $scope.page.number > 0 && $scope.page.number == $scope.page.totalPages) {
+ sessionStorage.setItem("list-page-number", --$scope.page.number);
+ }
+
$scope.page.content.splice($index, 1);
- var columnTitle = "Exclude";
+
var value = row.id.toString();
var gloss = "Submission #" + row.id;
- $scope.activeFilters.addFilter(columnTitle, value, gloss, true).then (function () {
+ $scope.activeFilters.addFilter(rowFilterTitle, value, gloss, true).then(function () {
query();
});
};
@@ -599,9 +663,11 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
};
$scope.displaySubmissionProperty = function (row, col) {
- var value = $scope.getSubmissionProperty(row, col);
+ if (angular.isDefined(col) && col !== null) {
+ return $filter('displayFieldValue')($scope.getSubmissionProperty(row, col), col.inputType);
+ }
- return angular.isDefined(col) ? $filter('displayFieldValue')(value, col.inputType) : value;
+ return value;
};
$scope.getCustomActionLabelById = function (id) {
@@ -635,12 +701,19 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
};
$scope.removeFilterValue = function (criterionName, filterValue) {
+ // Reset filter except for when row filters are in use.
+ if (criterionName !== rowFilterTitle) {
+ $scope.resetPagination();
+ }
+
$scope.activeFilters.removeFilter(criterionName, filterValue).then(function () {
query();
});
};
$scope.clearFilters = function () {
+ $scope.resetPagination();
+
$scope.activeFilters.clearFilters().then(function () {
query();
});
@@ -696,7 +769,9 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
updateChange(false);
};
- $scope.removeFilter = function (filter) {
+ $scope.removeSaveFilter = function (filter) {
+ $scope.resetPagination();
+
SavedFilterRepo.delete(filter).then(function () {
SavedFilterRepo.reset();
});
@@ -721,10 +796,8 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
};
$scope.resetColumns = function () {
- ManagerSubmissionListColumnRepo.reset();
- update();
+ update(true);
$scope.closeModal();
- updateChange(false);
};
$scope.resetColumnsToDefault = function () {
@@ -734,20 +807,23 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
};
$scope.saveColumns = function () {
- ManagerSubmissionListColumnRepo.updateSubmissionListColumns($scope.userColumns, $scope.page.count).then(function () {
- $scope.page.number = 1;
- sessionStorage.setItem("list-page-size", $scope.page.count);
- $scope.resetColumns();
+ ManagerSubmissionListColumnRepo.updateSubmissionListColumns($scope.userColumns, $scope.page.count).then(function (res) {
+ var results = angular.fromJson(res.body);
+ if (results.meta.status === 'SUCCESS') {
+ $scope.resetPagination();
+ sessionStorage.setItem("list-page-size", $scope.page.count);
+ $scope.resetColumns();
+ }
});
};
$scope.saveUserFilters = function () {
- ManagerFilterColumnRepo.updateFilterColumns(filterColumns.userFilterColumns).then(function () {
- for (var i in filterColumns.userFilterColumns) {
- delete filterColumns.userFilterColumns[i].status;
+ ManagerFilterColumnRepo.updateFilterColumns(filterColumns.userFilterColumns).then(function (res) {
+ var results = angular.fromJson(res.body);
+ if (results.meta.status === 'SUCCESS') {
+ update(false);
+ $scope.closeModal();
}
- update();
- $scope.closeModal();
});
};
@@ -888,13 +964,20 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle
"resetSaveUserFilters": $scope.resetSaveFilter,
"applyFilter": $scope.applyFilter,
"resetRemoveFilters": $scope.resetRemoveFilters,
- "removeFilter": $scope.removeFilter,
+ "removeSaveFilter": $scope.removeSaveFilter,
"getUserById": $scope.getUserById
},
$scope.furtherFilterBy,
$scope.advancedfeaturesBox
]);
+ $scope.resetPagination = function() {
+ $scope.pageNumber = 0;
+ $scope.page.number = 1;
+
+ sessionStorage.setItem("list-page-number", 1);
+ };
+
});
});
diff --git a/src/main/webapp/app/directives/tooltipDirective.js b/src/main/webapp/app/directives/tooltipDirective.js
index 75ff191ca3..61a656fccc 100644
--- a/src/main/webapp/app/directives/tooltipDirective.js
+++ b/src/main/webapp/app/directives/tooltipDirective.js
@@ -23,7 +23,7 @@ vireo.directive('tooltip', function ($timeout) {
}, 250);
};
- $scope.mouseEnter = function() {
+ $scope.showTooltip = function() {
open();
$timeout(function() {
angular.element('.popover').hover(function() {
@@ -34,7 +34,7 @@ vireo.directive('tooltip', function ($timeout) {
});
};
- $scope.mouseLeave = function() {
+ $scope.hideTooltip = function() {
close();
};
diff --git a/src/main/webapp/app/directives/validatedInputDirective.js b/src/main/webapp/app/directives/validatedInputDirective.js
index d229990094..f5e5c3f2c5 100644
--- a/src/main/webapp/app/directives/validatedInputDirective.js
+++ b/src/main/webapp/app/directives/validatedInputDirective.js
@@ -73,9 +73,9 @@ vireo.directive("validatedinput", function ($q, $timeout) {
}
// escape(27): reset value using shadow
else if ($event.which == 27) {
- $scope.model.refresh();
- } else {
-
+ if ($scope.model && typeof $scope.model.refresh === 'function') {
+ $scope.model.refresh();
+ }
}
};
diff --git a/src/main/webapp/app/filters/displayFieldValue.js b/src/main/webapp/app/filters/displayFieldValue.js
index 3d1ab79f1f..3ee5b0c78a 100644
--- a/src/main/webapp/app/filters/displayFieldValue.js
+++ b/src/main/webapp/app/filters/displayFieldValue.js
@@ -1,7 +1,7 @@
vireo.filter('displayFieldValue', function($filter, InputTypes) {
return function(value, inputType) {
if (angular.isUndefined(inputType)) {
- return value;
+ return value;
}
var dateColumn = null;
@@ -15,7 +15,7 @@ vireo.filter('displayFieldValue', function($filter, InputTypes) {
if (type != null) {
if (inputType.name == InputTypes.INPUT_LICENSE || inputType.name == InputTypes.INPUT_PROQUEST) {
- return value == 'true' ? 'yes' : 'no';
+ return value == 'true' ? 'yes' : 'no';
}
if (angular.isDefined(appConfig.dateColumns) && angular.isDefined(inputType.name)) {
@@ -35,17 +35,28 @@ vireo.filter('displayFieldValue', function($filter, InputTypes) {
}
}
- if (dateColumn == null || angular.isUndefined(value) || value == null) {
- return value;
+ if (dateColumn === null || angular.isUndefined(value) || value === null) {
+ return value;
}
// Some browsers, like Firefox, do not support 'MMMM yyyy' formats for Date.parse().
var stamp = Date.parse(value);
if (isNaN(stamp) && dateColumn.format == 'MMMM yyyy') {
var split = value.match(/^(\S+) (\d+)$/);
+
+ if (split === null || split.length < 3) {
+ return value;
+ }
+
return $filter('date')(new Date(split[1] + ' 01, ' + split[2]).toISOString(), dateColumn.format, 'utc');
}
- return $filter('date')(new Date(value).toISOString(), dateColumn.format, 'utc');
+ var date = new Date(value);
+
+ if (isNaN(date)) {
+ return value;
+ }
+
+ return $filter('date')(date.toISOString(), dateColumn.format, 'utc');
};
});
diff --git a/src/main/webapp/app/model/submission.js b/src/main/webapp/app/model/submission.js
index 2dc938dd89..3caceb95cf 100644
--- a/src/main/webapp/app/model/submission.js
+++ b/src/main/webapp/app/model/submission.js
@@ -514,7 +514,7 @@ var submissionModel = function ($filter, $q, ActionLog, FieldValue, FileService,
'uri': uri
}
});
- var promise = FileService.download(this.getMapping().file);
+ var promise = FileService.anonymousDownload(this.getMapping().file);
return promise;
};
diff --git a/src/main/webapp/app/resources/styles/sass/app.scss b/src/main/webapp/app/resources/styles/sass/app.scss
index ffca4bb104..efa7378053 100644
--- a/src/main/webapp/app/resources/styles/sass/app.scss
+++ b/src/main/webapp/app/resources/styles/sass/app.scss
@@ -12,7 +12,7 @@ html {
body {
font-family: "Trebuchet MS", Verdana, Geneva, Arial, Helvetica, Sans-Serif;
- color: #70706f;
+ color: $text_main_color;
margin-right: 0 !important;
padding-right: 0 !important;
height: 100%;
@@ -76,19 +76,18 @@ main footer {
}
.tab-nav li {
- background: $button_highlight_color_off;
+ background: $admin_tab_main_color;
padding: 0 10px;
margin-right: 15px;
min-width: 75px;
text-align: center;
- color: #1D3541;
+ color: $admin_tab_main_text_color;
font-size: 1.75em;
font-weight: bolder;
display: inline-block;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
box-shadow: inset 0 -5px 7px -5px rgba(0, 0, 0, 0.75);
- text-shadow: 0 0 10px rgba(255, 255, 245, 1), -1px -1px 0 rgba(250, 250, 235, 0.25), 1px 1px 0 rgba(250, 250, 235, 0.5);
}
.tab-nav li.settings-tab {
@@ -101,8 +100,8 @@ main footer {
.tab-nav li.active,
.tab-nav li:hover {
cursor: pointer;
- background: #FFFFFF;
- text-shadow: 0 0 0 #FFFFFF;
+ color: $admin_tab_selected_text_color;
+ background: $admin_tab_selected_color;
box-shadow: inset 0 0 0 #FFFFFF;
}
@@ -113,8 +112,7 @@ main footer {
.tab-nav li a {
height: 45px;
color: inherit;
- border-top-left-radius: 10px;
- border-top-right-radius: 10px;
+ background-color:transparent !important;
}
.tab-nav li.settings-tab a {
@@ -154,6 +152,7 @@ main footer {
}
.modal-header-primary {
+ color: $background_header_text_color;
background-color: $background_highlight_color;
}
@@ -393,12 +392,12 @@ tr.submission-list-row:hover td span.glyphicon.glyphicon-remove-circle {
.navbar-default .navbar-nav > .active > a,
.navbar-default .navbar-nav > .active > a:focus,
.navbar-default .navbar-nav > .active > a:hover {
- color: #fff;
+ color: $background_header_text_color;
background-color: $background_highlight_color;
}
.navbar-default .navbar-nav > li > a {
- color: #fff;
+ color: $background_header_text_color;
}
.navbar-default .navbar-brand {
@@ -586,7 +585,7 @@ togglebutton {
}
.toggle-button .btn.active {
- background-color: #a2ccb5;
+ background-color: #697A70;
color: #FFFFFF;
border: 1px solid #70706f;
outline: none;
@@ -684,26 +683,26 @@ h4 {
padding-top: 35px;
width: 250px;
height: 150px;
- color: #a6a6a6;
+ color: #696969;
border-radius: 5px;
transition: all 0.35s ease-in;
}
.trash-drop-zone.dragging {
transition: all 0.35s ease-in;
- border-color: #a6a6a6;
+ border-color: #696969;
}
.trash-drop-zone .glyphicon-trash {
transition: all 0.35s ease-in;
color: #fefefe;
- text-shadow: 1px 0 5px #a6a6a6;
+ text-shadow: 1px 0 5px #696969;
font-size: 70px;
}
.trash-drop-zone.dragging .glyphicon-trash {
transition: all 0.35s ease-in;
- color: #a6a6a6;
+ color: #696969;
text-shadow: 1px 0 5px #fefefe;
font-size: 70px;
}
@@ -715,20 +714,20 @@ h4 {
padding-top: 35px;
max-width: 250px;
height: 150px;
- color: #a6a6a6;
+ color: #696969;
border-radius: 5px;
transition: all 0.35s ease-in;
}
.upload-drop-zone.dragging-accept {
transition: all 0.35s ease-in;
- border-color: #a6a6a6;
+ border-color: #696969;
}
.upload-drop-zone .glyphicon-upload {
transition: all 0.35s ease-in;
color: #fefefe;
- text-shadow: 1px 0 5px #a6a6a6;
+ text-shadow: 1px 0 5px #696969;
font-size: 70px;
}
@@ -739,7 +738,7 @@ h4 {
.upload-drop-zone.dragging .glyphicon-upload,
.upload-drop-zone.reject .glyphicon-ban-circle {
transition: all 0.35s ease-in;
- color: #a6a6a6;
+ color: #696969;
text-shadow: 1px 0 5px #fefefe;
font-size: 70px;
}
@@ -842,7 +841,7 @@ legend.legendborder {
footer a,
footer li,
footer p {
- color: #fff;
+ color: $background_footer_text_color;
font-size: 0.9em;
}
@@ -956,6 +955,22 @@ input[type=color]:focus {
/**************************
* LOOK AND FEEL ACCORDION *
***************************/
+.look-and-feel-h1 {
+ font-size: 18px;
+ font-style: bold;
+ margin-bottom: .2em;
+}
+
+.look-and-feel-h2 {
+ font-size: 14px;
+ margin-bottom: 1.5em;
+}
+
+.look-and-feel-h1 .glyphicon.glyphicon-info-sign {
+ vertical-align: text-top;
+ font-size: 12px;
+}
+
.look-and-feel .color-reset {
text-indent: 0;
font-size: 0.85em;
@@ -972,11 +987,12 @@ input[type=color]:focus {
}
.look-and-feel .color-input {
- height: 37px;
+ height: 42px;
+ min-width: 78px;
}
.look-and-feel .color-selector {
- max-width: 200px;
+ max-width: 100px;
}
.look-and-feel label.color-selector-swatch {
@@ -1194,17 +1210,17 @@ input[type=color]:focus {
.triptych-panel .list-group-item:hover {
cursor: pointer;
- color: #70706f;
+ color: $text_main_color;
background: #efefef;
}
.triptych-panel.previously-active .list-group-item.selected {
- color: #70706f;
+ color: $text_main_color;
background: #E6ECE8;
}
.triptych-panel .list-group-item.selected {
- color: #fff;
+ color: $text_main_color;
background: #92AA9C;
}
@@ -1423,6 +1439,7 @@ input[type=color]:focus {
* NOTES *
*********/
.note {
+ color: #474747;
position: relative;
max-width: 225px;
padding: 15px 15px 35px 35px;
diff --git a/src/main/webapp/app/resources/styles/sass/directives/_tooltip.scss b/src/main/webapp/app/resources/styles/sass/directives/_tooltip.scss
index ed3ccf8c7a..b0c26e3b08 100644
--- a/src/main/webapp/app/resources/styles/sass/directives/_tooltip.scss
+++ b/src/main/webapp/app/resources/styles/sass/directives/_tooltip.scss
@@ -37,6 +37,7 @@
background: -webkit-linear-gradient(to bottom, rgba(31, 58, 71, 0.95), rgba(80, 114, 129, 0.95));
background: -ms-linear-gradient(to bottom, rgba(31, 58, 71, 0.95), rgba(80, 114, 129, 0.95));
background: linear-gradient(to bottom, rgba(31, 58, 71, 0.95), rgba(80, 114, 129, 0.95));
+ opacity: 1 !important;
}
.tooltip .popover-content a,
diff --git a/src/main/webapp/app/resources/styles/sass/overrides/_bootstrap.scss b/src/main/webapp/app/resources/styles/sass/overrides/_bootstrap.scss
index cfa3fafc74..3b79d26f6d 100644
--- a/src/main/webapp/app/resources/styles/sass/overrides/_bootstrap.scss
+++ b/src/main/webapp/app/resources/styles/sass/overrides/_bootstrap.scss
@@ -6,10 +6,10 @@
// $gray-light: lighten($gray-base, 46.7%) !default; // #777
// $gray-lighter: lighten($gray-base, 93.5%) !default; // #eee
-$brand-primary: darken(#428bca, 6.5%) !default; // #337ab7
+$brand-primary: darken(#367DC4, 6.5%) !default; // #337ab7
// $brand-success: #5cb85c !default;
-// $brand-info: #5bc0de !default;
+$brand-info: #357AB9 !default;
// $brand-warning: #f0ad4e !default;
-$brand-danger: #CD7674 !default;
+$brand-danger: #C15453 !default;
-$icon-font-path: "../fonts/bootstrap/" !default;
+$icon-font-path: "../fonts/bootstrap/" !default;
\ No newline at end of file
diff --git a/src/main/webapp/app/resources/styles/sass/views/admin/_view.scss b/src/main/webapp/app/resources/styles/sass/views/admin/_view.scss
index ce371266c2..2cb0b007be 100644
--- a/src/main/webapp/app/resources/styles/sass/views/admin/_view.scss
+++ b/src/main/webapp/app/resources/styles/sass/views/admin/_view.scss
@@ -212,3 +212,6 @@ div .item-view div.row h1 span.spinning {
color: #555;
}
+.list-view-table-controls {
+ margin-bottom: 5px;
+}
diff --git a/src/main/webapp/app/resources/styles/sass/views/submission/_studentSubmission.scss b/src/main/webapp/app/resources/styles/sass/views/submission/_studentSubmission.scss
index 24323c2d6a..30e3dbd11a 100644
--- a/src/main/webapp/app/resources/styles/sass/views/submission/_studentSubmission.scss
+++ b/src/main/webapp/app/resources/styles/sass/views/submission/_studentSubmission.scss
@@ -70,7 +70,7 @@
padding: 10px 15px;
border-radius: 4px;
width: 100%;
- color: white;
+ color: $button_text_color_off;
background: $button_main_color_off;
background: -moz-linear-gradient linear-gradient(to right, $button_main_color_off, $button_highlight_color_off);
background: -webkit-linear-gradient(to right, $button_main_color_off, $button_highlight_color_off);
@@ -82,6 +82,7 @@
.submission-workflow-step-navigation a .invisible-button:focus,
.submission-workflow-step-navigation.active a .invisible-button {
cursor: pointer;
+ color: $button_text_color_on;
background: rgb(31, 58, 71);
background: -moz-linear-gradient linear-gradient(to right, $button_main_color_on, $button_highlight_color_on);
background: -webkit-linear-gradient(to right, $button_main_color_on, $button_highlight_color_on);
diff --git a/src/main/webapp/app/resources/styles/sass/views/submission/_submissionAdvisor.scss b/src/main/webapp/app/resources/styles/sass/views/submission/_submissionAdvisor.scss
index f207cf02fc..7f1b4734da 100644
--- a/src/main/webapp/app/resources/styles/sass/views/submission/_submissionAdvisor.scss
+++ b/src/main/webapp/app/resources/styles/sass/views/submission/_submissionAdvisor.scss
@@ -6,3 +6,9 @@
margin-left: 0;
margin-right: 0;
}
+
+.file-link {
+ color: #337ab7;
+ cursor: pointer;
+ text-decoration: none;
+}
diff --git a/src/main/webapp/app/resources/styles/sass/views/submission/_submissionDialog.scss b/src/main/webapp/app/resources/styles/sass/views/submission/_submissionDialog.scss
index 50236b8309..24589b3d8b 100644
--- a/src/main/webapp/app/resources/styles/sass/views/submission/_submissionDialog.scss
+++ b/src/main/webapp/app/resources/styles/sass/views/submission/_submissionDialog.scss
@@ -18,7 +18,7 @@ submissiondialog div.dialog {
}
@media (min-width: 1540px) {
submissiondialog div.dialog {
- box-shadow: 3px 3px 10px -6px #000000;
+ box-shadow: 3px 3px 10px -6px #757575;
}
}
diff --git a/src/main/webapp/app/views/admin/list.html b/src/main/webapp/app/views/admin/list.html
index 9841a38494..fcbdd573f3 100644
--- a/src/main/webapp/app/views/admin/list.html
+++ b/src/main/webapp/app/views/admin/list.html
@@ -1,12 +1,20 @@
-
+
List ETDs
-
Customize view
+
+
+
+ Customize columns
+
+
+ Default columns
+
+