prepared file upload

This commit is contained in:
Jottyfan 2023-01-28 18:40:23 +01:00
parent e6e625306d
commit 9e7850a4a1
37 changed files with 625 additions and 76 deletions

View File

@ -18,7 +18,7 @@ apply plugin: 'war'
apply plugin: 'application' apply plugin: 'application'
group = 'de.jottyfan.camporganizer' group = 'de.jottyfan.camporganizer'
version = '0.2.8' version = '0.2.9'
sourceCompatibility = 17 sourceCompatibility = 17
mainClassName = "de.jottyfan.camporganizer.Main" mainClassName = "de.jottyfan.camporganizer.Main"

View File

@ -13,6 +13,7 @@ import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError; import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
@ -34,6 +35,17 @@ public class AdminController extends CommonController {
@Value("${spring.mail.username}") @Value("${spring.mail.username}")
private String from; private String from;
@GetMapping("/admin")
public String getMain(Model model, HttpServletRequest request) {
super.setupSession(model, request);
return "/admin/main";
}
@GetMapping("/admin/main")
public String getMainDirectly() {
return "redirect:/admin/main";
}
@GetMapping("/admin/mail") @GetMapping("/admin/mail")
public String getMail(Model model, HttpServletRequest request) { public String getMail(Model model, HttpServletRequest request) {
super.setupSession(model, request); super.setupSession(model, request);
@ -57,14 +69,37 @@ public class AdminController extends CommonController {
return "redirect:/admin"; return "redirect:/admin";
} }
@GetMapping("/admin") @GetMapping("/admin/document")
public String getMain(Model model, HttpServletRequest request) { public String getDocuments(Model model, HttpServletRequest request) {
super.setupSession(model, request); super.setupSession(model, request);
return "/admin/main"; model.addAttribute("documents", service.getAllDocuments());
return "/admin/document";
} }
@GetMapping("/admin/main") @GetMapping("/admin/document/add")
public String getMainDirectly() { public String prepareAddDocument(Model model, HttpServletRequest request) {
return "redirect:/admin/main"; super.setupSession(model, request);
model.addAttribute("bean", new DocumentBean());
return "/admin/document_edit";
}
@GetMapping("/admin/document/edit/{id}")
public String prepareAddDocument(@PathVariable Integer id, Model model, HttpServletRequest request) {
super.setupSession(model, request);
model.addAttribute("bean", service.getDocument(id));
return "/admin/document_edit";
}
@PostMapping("/admin/document/update")
public String updateDocument(@Valid @ModelAttribute("bean") DocumentBean bean,
final BindingResult bindingResult, Model model, HttpServletRequest request) {
super.setupSession(model, request);
if (bindingResult.hasErrors()) {
for (ObjectError error : bindingResult.getAllErrors())
LOGGER.error("error {}: {}", error.getCode(), error.getDefaultMessage());
return "/admin/document_edit";
}
service.updateDocument(bean);
return "redirect:/admin/document";
} }
} }

View File

@ -0,0 +1,186 @@
package de.jottyfan.camporganizer.module.admin;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENTROLE;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep;
import org.jooq.Field;
import org.jooq.InsertResultStep;
import org.jooq.InsertReturningStep;
import org.jooq.Record5;
import org.jooq.SelectSeekStep1;
import org.jooq.UpdateConditionStep;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.db.jooq.enums.EnumFiletype;
import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentroleRecord;
import de.jottyfan.camporganizer.module.camplist.LambdaResultWrapper;
/**
*
* @author jotty
*
*/
@Repository
@Transactional(transactionManager = "transactionManager")
public class AdminRepository {
private static final Logger LOGGER = LogManager.getLogger(AdminRepository.class);
@Autowired
private DSLContext jooq;
/**
* get the document with that ID
*
* @param id the ID of the document
* @return the document
*/
public DocumentBean getDocument(Integer id) {
Field<EnumCamprole[]> ROLES = DSL.field("roles", EnumCamprole[].class);
SelectSeekStep1<Record5<Integer, String, EnumDocument, EnumFiletype, EnumCamprole[]>, Integer> sql = jooq
// @formatter:off
.select(T_DOCUMENT.PK,
T_DOCUMENT.NAME,
T_DOCUMENT.DOCTYPE,
T_DOCUMENT.FILETYPE,
DSL.arrayAgg(T_DOCUMENTROLE.CAMPROLE).as(ROLES))
.from(T_DOCUMENT)
.leftJoin(T_DOCUMENTROLE).on(T_DOCUMENTROLE.FK_DOCUMENT.eq(T_DOCUMENT.PK))
.where(T_DOCUMENT.PK.eq(id))
.groupBy(T_DOCUMENT.PK, T_DOCUMENT.NAME, T_DOCUMENT.DOCTYPE, T_DOCUMENT.FILETYPE)
.orderBy(T_DOCUMENT.PK);
// @formatter:on
LOGGER.debug(sql.toString());
Record5<Integer, String, EnumDocument, EnumFiletype, EnumCamprole[]> r = sql.fetchOne();
if (r != null ) {
DocumentBean bean = new DocumentBean();
bean.setPk(r.get(T_DOCUMENT.PK));
bean.setName(r.get(T_DOCUMENT.NAME));
bean.setDoctype(r.get(T_DOCUMENT.DOCTYPE));
bean.setFiletype(r.get(T_DOCUMENT.FILETYPE));
bean.setRoles(r.get(ROLES));
return bean;
}
return null;
}
/**
* get all documents from the database
*
* @return all documents
*/
public List<DocumentBean> getAllDocuments() {
Field<EnumCamprole[]> ROLES = DSL.field("roles", EnumCamprole[].class);
SelectSeekStep1<Record5<Integer, String, EnumDocument, EnumFiletype, EnumCamprole[]>, Integer> sql = jooq
// @formatter:off
.select(T_DOCUMENT.PK,
T_DOCUMENT.NAME,
T_DOCUMENT.DOCTYPE,
T_DOCUMENT.FILETYPE,
DSL.arrayAgg(T_DOCUMENTROLE.CAMPROLE).as(ROLES))
.from(T_DOCUMENT)
.leftJoin(T_DOCUMENTROLE).on(T_DOCUMENTROLE.FK_DOCUMENT.eq(T_DOCUMENT.PK))
.groupBy(T_DOCUMENT.PK, T_DOCUMENT.NAME, T_DOCUMENT.DOCTYPE, T_DOCUMENT.FILETYPE)
.orderBy(T_DOCUMENT.PK);
// @formatter:on
LOGGER.debug(sql.toString());
List<DocumentBean> list = new ArrayList<>();
for (Record5<Integer, String, EnumDocument, EnumFiletype, EnumCamprole[]> r : sql.fetch()) {
DocumentBean bean = new DocumentBean();
bean.setPk(r.get(T_DOCUMENT.PK));
bean.setName(r.get(T_DOCUMENT.NAME));
bean.setDoctype(r.get(T_DOCUMENT.DOCTYPE));
bean.setFiletype(r.get(T_DOCUMENT.FILETYPE));
bean.setRoles(r.get(ROLES));
list.add(bean);
}
return list;
}
/**
* upsert document in t_document
*
* @param document
* @throws DataAccessException
*/
public Integer upsert(DocumentBean bean) throws DataAccessException {
LambdaResultWrapper lrw = new LambdaResultWrapper();
jooq.transaction(c -> {
Integer pk = bean.getPk();
if (bean.getPk() != null) {
UpdateConditionStep<TDocumentRecord> sql = DSL.using(c)
// @formatter:off
.update(T_DOCUMENT)
.set(T_DOCUMENT.NAME, bean.getName())
.set(T_DOCUMENT.DOCTYPE, bean.getDoctype())
.set(T_DOCUMENT.DOCUMENT, bean.getDocument())
.set(T_DOCUMENT.FILETYPE, bean.getFiletype())
.where(T_DOCUMENT.PK.eq(bean.getPk()));
// @formatter:on
LOGGER.debug("{}", sql.toString());
lrw.add(sql.execute());
} else {
InsertResultStep<TDocumentRecord> sql = DSL.using(c)
// @formatter:off
.insertInto(T_DOCUMENT,
T_DOCUMENT.NAME,
T_DOCUMENT.DOCTYPE,
T_DOCUMENT.DOCUMENT,
T_DOCUMENT.FILETYPE)
.values(bean.getName(), bean.getDoctype(), bean.getDocument(), bean.getFiletype())
.returning(T_DOCUMENT.PK);
// @formatter:on
LOGGER.debug("{}", sql.toString());
pk = sql.fetchOne().get(T_DOCUMENT.PK);
lrw.add(1);
}
List<EnumCamprole> allEnums = Arrays.asList(EnumCamprole.values());
Set<EnumCamprole> removeCandidates = new HashSet<>();
removeCandidates.addAll(allEnums);
for (EnumCamprole role : bean.getRoles()) {
try {
InsertReturningStep<TDocumentroleRecord> sql = DSL.using(c)
// @formatter:off
.insertInto(T_DOCUMENTROLE,
T_DOCUMENTROLE.FK_DOCUMENT,
T_DOCUMENTROLE.CAMPROLE)
.values(pk, role)
.onConflict(T_DOCUMENTROLE.FK_DOCUMENT, T_DOCUMENTROLE.CAMPROLE)
.doNothing();
// @formatter:on
LOGGER.debug("{}", sql.toString());
lrw.add(sql.execute());
removeCandidates.remove(role);
} catch (IllegalArgumentException e) {
LOGGER.error(e);
}
}
DeleteConditionStep<TDocumentroleRecord> sql = DSL.using(c)
// @formatter:off
.deleteFrom(T_DOCUMENTROLE)
.where(T_DOCUMENTROLE.FK_DOCUMENT.eq(pk))
.and(T_DOCUMENTROLE.CAMPROLE.in(removeCandidates));
// @formatter:on
LOGGER.debug("{}", sql.toString());
lrw.add(sql.execute());
});
return lrw.getCounter();
}
}

View File

@ -1,5 +1,7 @@
package de.jottyfan.camporganizer.module.admin; package de.jottyfan.camporganizer.module.admin;
import java.util.List;
import javax.validation.Valid; import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -19,8 +21,39 @@ public class AdminService {
@Autowired @Autowired
private MailRepository mailRepository; private MailRepository mailRepository;
@Autowired
private AdminRepository adminRepository;
public void sendMail(@Valid MailBean bean) { public void sendMail(@Valid MailBean bean) {
mailRepository.sendMail(bean); mailRepository.sendMail(bean);
} }
/**
* get all documents
*
* @return a list of documents; an empty one at least
*/
public List<DocumentBean> getAllDocuments() {
return adminRepository.getAllDocuments();
}
/**
* get document from the database
*
* @param id the ID of the document
* @return the document or null
*/
public DocumentBean getDocument(Integer id) {
return adminRepository.getDocument(id);
}
/**
* upsert the document bean
*
* @param bean the bean
* @return the number of affected database lines
*/
public Integer updateDocument(@Valid DocumentBean bean) {
return adminRepository.upsert(bean);
}
} }

View File

@ -0,0 +1,107 @@
package de.jottyfan.camporganizer.module.admin;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Base64;
import javax.servlet.http.Part;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.apache.commons.io.IOUtils;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.db.jooq.enums.EnumFiletype;
/**
*
* @author jotty
*
*/
public class DocumentBean implements Serializable{
private static final long serialVersionUID = 1L;
private Integer pk;
@NotNull
private EnumDocument doctype;
@NotBlank
private String name;
private String document;
@NotNull
private EnumFiletype filetype;
@NotNull
private Part uploadfile;
private EnumCamprole[] roles;
public void encodeUpload() throws IOException {
if (uploadfile != null) {
InputStream inputStream = uploadfile.getInputStream();
byte[] bytes = IOUtils.toByteArray(inputStream);
if (bytes.length > 0) {
document = Base64.getEncoder().encodeToString(bytes);
} // not uploaded files should not be changed, so document must be kept as is
}
}
public Integer getPk() {
return pk;
}
public void setDoctype(EnumDocument doctype) {
this.doctype = doctype;
}
public EnumDocument getDoctype() {
return doctype;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setDocument(String document) {
this.document = document;
}
public String getDocument() {
return document;
}
public void setFiletype(EnumFiletype filetype) {
this.filetype = filetype;
}
public EnumFiletype getFiletype() {
return filetype;
}
public Part getUploadfile() {
return uploadfile;
}
public void setUploadfile(Part uploadfile) {
this.uploadfile = uploadfile;
}
public EnumCamprole[] getRoles() {
return roles;
}
public void setRoles(EnumCamprole[] roles) {
this.roles = roles;
}
/**
* @param pk the pk to set
*/
public void setPk(Integer pk) {
this.pk = pk;
}
}

View File

@ -34,8 +34,8 @@ import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class BookingsGateway { public class BookingsRepository {
private static final Logger LOGGER = LogManager.getLogger(BookingsGateway.class); private static final Logger LOGGER = LogManager.getLogger(BookingsRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -16,7 +16,7 @@ import de.jottyfan.camporganizer.module.business.bookings.IBookingsService;
public class BookingsService implements IBookingsService { public class BookingsService implements IBookingsService {
@Autowired @Autowired
private BookingsGateway bookingsGateway; private BookingsRepository bookingsGateway;
@Override @Override
public List<BookerBean> getBookers(String username) { public List<BookerBean> getBookers(String username) {

View File

@ -33,8 +33,8 @@ import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class BusinessGateway { public class BusinessRepository {
private static final Logger LOGGER = LogManager.getLogger(BusinessGateway.class); private static final Logger LOGGER = LogManager.getLogger(BusinessRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -19,7 +19,7 @@ import de.jottyfan.camporganizer.module.business.business.IBusinessService;
@Service @Service
public class BusinessService implements IBusinessService { public class BusinessService implements IBusinessService {
@Autowired @Autowired
private BusinessGateway gateway; private BusinessRepository gateway;
@Override @Override
public String getCurrentUser(HttpServletRequest request) { public String getCurrentUser(HttpServletRequest request) {

View File

@ -32,8 +32,8 @@ import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class CampGateway { public class CampRepository {
private static final Logger LOGGER = LogManager.getLogger(CampGateway.class); private static final Logger LOGGER = LogManager.getLogger(CampRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -17,7 +17,7 @@ import de.jottyfan.camporganizer.module.business.camp.ICampService;
public class CampService implements ICampService { public class CampService implements ICampService {
@Autowired @Autowired
private CampGateway campGateway; private CampRepository campGateway;
@Override @Override
public CampBean getCamp(Integer id, String username) { public CampBean getCamp(Integer id, String username) {

View File

@ -29,8 +29,8 @@ import de.jottyfan.camporganizer.db.jooq.tables.records.TSalesprofileRecord;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class PrivilegesGateway { public class PrivilegesRepository {
private static final Logger LOGGER = LogManager.getLogger(PrivilegesGateway.class); private static final Logger LOGGER = LogManager.getLogger(PrivilegesRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -18,7 +18,7 @@ import de.jottyfan.camporganizer.module.business.privileges.IPrivilegesService;
@Service @Service
public class PrivilegesService implements IPrivilegesService { public class PrivilegesService implements IPrivilegesService {
@Autowired @Autowired
private PrivilegesGateway gateway; private PrivilegesRepository gateway;
@Override @Override
public Map<Integer, CampBean> getPrivileges() { public Map<Integer, CampBean> getPrivileges() {

View File

@ -13,7 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord;
import de.jottyfan.camporganizer.module.dashboard.DashboardGateway; import de.jottyfan.camporganizer.module.dashboard.DashboardRepository;
/** /**
* *
@ -27,7 +27,7 @@ public class CamplistService {
private CamplistGateway gateway; private CamplistGateway gateway;
@Autowired @Autowired
private DashboardGateway dashboardGateway; private DashboardRepository dashboardGateway;
/** /**
* get all camps from the database and prepare them for the view * get all camps from the database and prepare them for the view

View File

@ -38,8 +38,8 @@ import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class ConfirmationGateway { public class ConfirmationRepository {
private static final Logger LOGGER = LogManager.getLogger(ConfirmationGateway.class); private static final Logger LOGGER = LogManager.getLogger(ConfirmationRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -19,7 +19,7 @@ import de.jottyfan.camporganizer.module.confirmation.confirmation.IConfirmationS
@Service @Service
public class ConfirmationService implements IConfirmationService { public class ConfirmationService implements IConfirmationService {
@Autowired @Autowired
private ConfirmationGateway gateway; private ConfirmationRepository gateway;
@Override @Override
public String getCurrentUser(HttpServletRequest request) { public String getCurrentUser(HttpServletRequest request) {

View File

@ -46,8 +46,8 @@ import de.jottyfan.camporganizer.module.mail.MailRepository;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class PersonGateway { public class PersonRepository {
private static final Logger LOGGER = LogManager.getLogger(PersonGateway.class); private static final Logger LOGGER = LogManager.getLogger(PersonRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -15,7 +15,7 @@ import de.jottyfan.camporganizer.module.confirmation.person.IPersonService;
@Service @Service
public class PersonService implements IPersonService { public class PersonService implements IPersonService {
@Autowired @Autowired
private PersonGateway gateway; private PersonRepository gateway;
@Override @Override
public PersonBean getPerson(String username, Integer pk) { public PersonBean getPerson(String username, Integer pk) {

View File

@ -31,8 +31,8 @@ import de.jottyfan.camporganizer.module.camplist.LambdaResultWrapper;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class DashboardGateway { public class DashboardRepository {
private static final Logger LOGGER = LogManager.getLogger(DashboardGateway.class); private static final Logger LOGGER = LogManager.getLogger(DashboardRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -30,8 +30,8 @@ import biweekly.property.Summary;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class ICalGateway { public class ICalRepository {
private static final Logger LOGGER = LogManager.getLogger(ICalGateway.class); private static final Logger LOGGER = LogManager.getLogger(ICalRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -20,7 +20,7 @@ import de.jottyfan.camporganizer.module.ical.IICalService;
public class ICalService implements IICalService { public class ICalService implements IICalService {
@Autowired @Autowired
private ICalGateway gateway; private ICalRepository gateway;
@Override @Override
public Boolean generate(HttpServletResponse response) throws IOException { public Boolean generate(HttpServletResponse response) throws IOException {

View File

@ -53,8 +53,8 @@ import de.jottyfan.camporganizer.module.camplist.LambdaResultWrapper;
*/ */
@Repository @Repository
@Transactional(transactionManager = "transactionManager") @Transactional(transactionManager = "transactionManager")
public class RegistrationGateway { public class RegistrationRepository {
private static final Logger LOGGER = LogManager.getLogger(RegistrationGateway.class); private static final Logger LOGGER = LogManager.getLogger(RegistrationRepository.class);
@Autowired @Autowired
private DSLContext jooq; private DSLContext jooq;

View File

@ -17,7 +17,7 @@ public class RegistrationService {
private final static Logger LOGGER = LogManager.getLogger(RegistrationService.class); private final static Logger LOGGER = LogManager.getLogger(RegistrationService.class);
@Autowired @Autowired
private RegistrationGateway gateway; private RegistrationRepository gateway;
@Autowired @Autowired
private KeycloakRepository keycloak; private KeycloakRepository keycloak;

View File

@ -6,7 +6,7 @@ import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import de.jottyfan.camporganizer.module.registration.RegistrationGateway; import de.jottyfan.camporganizer.module.registration.RegistrationRepository;
/** /**
* *
@ -19,7 +19,7 @@ public class UnusedUsernameValidator implements ConstraintValidator<UnusedUserna
private String message; private String message;
@Autowired @Autowired
private RegistrationGateway gateway; private RegistrationRepository gateway;
public void initialize(UnusedUsername uu) { public void initialize(UnusedUsername uu) {
this.field = uu.field(); this.field = uu.field();

View File

@ -4,11 +4,16 @@
font-weight: 700; font-weight: 700;
font-display: swap; font-display: swap;
src: url('../fonts/Cabin-Sketch-700.eot'); /* IE9 */ src: url('../fonts/Cabin-Sketch-700.eot'); /* IE9 */
src: url('../fonts/Cabin-Sketch-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ src: url('../fonts/Cabin-Sketch-700.eot?#iefix')
url('../fonts/Cabin-Sketch-700.woff2') format('woff2'), /* Modern Browsers */ format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/Cabin-Sketch-700.woff') format('woff'), /* Modern Browsers */ url('../fonts/Cabin-Sketch-700.woff2') format('woff2'),
url('../fonts/Cabin-Sketch-700.ttf') format('truetype'), /* Safari, Android, iOS */ /* Modern Browsers */
url('../fonts/Cabin-Sketch-700.svg#CabinSketch') format('svg'); /* Legacy iOS */ url('../fonts/Cabin-Sketch-700.woff') format('woff'),
/* Modern Browsers */
url('../fonts/Cabin-Sketch-700.ttf') format('truetype'),
/* Safari, Android, iOS */
url('../fonts/Cabin-Sketch-700.svg#CabinSketch') format('svg');
/* Legacy iOS */
} }
/* fira-sans-regular - latin */ /* fira-sans-regular - latin */
@ -16,13 +21,18 @@
font-family: 'Fira Sans'; font-family: 'Fira Sans';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: url('../fonts/fira-sans-v16-latin-regular.eot'); /* IE9 Compat Modes */ src: url('../fonts/fira-sans-v16-latin-regular.eot');
src: local(''), /* IE9 Compat Modes */
url('../fonts/fira-sans-v16-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ src: local(''), url('../fonts/fira-sans-v16-latin-regular.eot?#iefix')
url('../fonts/fira-sans-v16-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/fira-sans-v16-latin-regular.woff') format('woff'), /* Modern Browsers */ url('../fonts/fira-sans-v16-latin-regular.woff2') format('woff2'),
url('../fonts/fira-sans-v16-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ /* Super Modern Browsers */
url('../fonts/fira-sans-v16-latin-regular.svg#FiraSans') format('svg'); /* Legacy iOS */ url('../fonts/fira-sans-v16-latin-regular.woff') format('woff'),
/* Modern Browsers */
url('../fonts/fira-sans-v16-latin-regular.ttf')
format('truetype'), /* Safari, Android, iOS */
url('../fonts/fira-sans-v16-latin-regular.svg#FiraSans')
format('svg'); /* Legacy iOS */
} }
html { html {
@ -34,6 +44,11 @@ body {
background-image: url('../images/background.jpg'); background-image: url('../images/background.jpg');
} }
.navbar-background {
background-image: url('../images/background.jpg');
background-repeat: no-repeat;
}
a { a {
font-family: 'Fira Sans'; font-family: 'Fira Sans';
} }
@ -96,19 +111,23 @@ div {
} }
.acc_over { .acc_over {
background-image: linear-gradient(to bottom right, #ccc, #aaa) !important; background-image: linear-gradient(to bottom right, #ccc, #aaa)
!important;
} }
.acc_true { .acc_true {
background-image: linear-gradient(to bottom right, #cfc, #afa) !important; background-image: linear-gradient(to bottom right, #cfc, #afa)
!important;
} }
.acc_false { .acc_false {
background-image: linear-gradient(to bottom right, #fcc, #faa) !important; background-image: linear-gradient(to bottom right, #fcc, #faa)
!important;
} }
.acc_null { .acc_null {
background-image: linear-gradient(to bottom right, #fdb, #fca) !important; background-image: linear-gradient(to bottom right, #fdb, #fca)
!important;
} }
.right-dist { .right-dist {
@ -360,3 +379,19 @@ div {
text-align: center; text-align: center;
padding-top: 45vh; padding-top: 45vh;
} }
.roleflag {
border-radius: 24px;
border: 1px solid gray;
padding: 4px;
margin: 2px;
background-image: linear-gradient(to right bottom, silver, white);
}
.tablebox {
margin: 8px;
background-color: rgba(255, 255, 255, 0.5) !important;
padding: 6px;
border-radius: 8px;
border: 1px solid gray;
}

View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<body>
<th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')">
<li class="nav-item"><a th:href="@{/rss/admin}" class="btn btn-seconary btn-icon-silent"><i class="fas fa-rss"></i></a></li>
</ul>
<ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul>
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')">
<li class="nav-item"><a th:href="@{/admin}" class="btn btn-secondary btn-icon-silent">Administration</a></li>
</ul>
</th:block>
<th:block layout:fragment="content">
<div class="tablebox" sec:authorize="hasRole('admin')">
<table id="docs" class="table table-striped" style="width: 100% !important">
<thead>
<tr>
<td>Name</td>
<td>Dokumententyp</td>
<td>Zielgruppe</td>
<th>Inhalt</th>
<th>Dateityp</th>
</tr>
</thead>
<tbody>
<tr th:each="d : ${documents}">
<td><a th:href="@{/admin/document/edit/{id}(id=${d.pk})}"><span th:text="${d.name}"></span></a></td>
<td><span th:if="${d.doctype.literal == 'location'}">Wegbeschreibung</span> <span th:if="${d.doctype.literal == 'camp'}">Bestätigung</span><span
th:if="${d.doctype.literal == 'camppass'}">Freizeitpass</span></td>
<td><th:block th:each="r : ${d.roles}">
<span th:if="${r.literal == 'student'}" class="roleflag">Teilnehmer</span>
<span th:if="${r.literal == 'teacher'}" class="roleflag">Mitarbeiter</span>
<span th:if="${r.literal == 'director'}" class="roleflag">Leiter</span>
<span th:if="${r.literal == 'feeder'}" class="roleflag">Küche</span>
</th:block></td>
<td><a th:href="@{/document/{id}(id=${d.pk})}"><i class="fas fa-download"></i></a></td>
<td><span th:text="${d.filetype.literal}" th:if="${d.filetype}"></span></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="6" style="text-align: center"><a th:href="@{/admin/document/add}" class="btn btn-outline-primary">neues Dokument anlegen</a></td>
</tr>
</tfoot>
</table>
<script>
$(document).ready(function() {
$("#docs").DataTable({
language : locale_de
});
});
</script>
</div>
</th:block>
</body>
</html>

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<body>
<th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')">
<li class="nav-item"><a th:href="@{/rss/admin}" class="btn btn-seconary btn-icon-silent"><i class="fas fa-rss"></i></a></li>
</ul>
<ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul>
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')">
<li class="nav-item"><a th:href="@{/admin}" class="btn btn-secondary btn-icon-silent">Administration</a></li>
</ul>
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')">
<li class="nav-item"><a th:href="@{/admin/document}" class="btn btn-secondary btn-icon-silent">Dokumente</a></li>
</ul>
</th:block>
<th:block layout:fragment="content">
<div sec:authorize="hasRole('admin')">
<form th:action="@{/admin/document/update}" th:object="${bean}" method="post" enctype="multipart/form-data">
<div class="tablebox">
<div class="container">
<input type="hidden" th:field="*{pk}" />
<div class="row mb-2">
<label for="inputName" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<span class="error" th:each="error : ${#fields.errors('name')}">[[${error}]]<br /></span> <input id="inputName" type="text" th:field="*{name}"
th:class="${'form-control ' + (#fields.hasErrors('name') ? 'inputerror' : '')}">
</div>
</div>
<div class="row mb-2">
<label for="inputDoctype" class="col-sm-2 col-form-label">Dokumententyp</label>
<div class="col-sm-10">
<span class="error" th:each="error : ${#fields.errors('doctype')}">[[${error}]]<br /></span> <select id="inputDoctype" th:field="*{doctype}"
th:class="${'form-select ' + (#fields.hasErrors('doctype') ? 'inputerror' : '')}">
<option value="">--- bitte wählen ---</option>
<option value="location">Wegbeschreibung</option>
<option value="camp">Bestätigung</option>
<option value="camppass">Freizeitpass</option>
</select>
</div>
</div>
<div class="row mb-2">
<label for="inputRole" class="col-sm-2 col-form-label">Zielgruppe</label>
<div class="col-sm-10">
<span class="error" th:each="error : ${#fields.errors('roles')}">[[${error}]]<br /></span> <select id="inputRole" th:field="*{roles}"
th:class="${'form-control ' + (#fields.hasErrors('roles') ? 'inputerror' : '')}" multiple>
<option value="student">Teilnehmer</option>
<option value="teacher">Mitarbeiter</option>
<option value="director">Leiter</option>
<option value="feeder">Küche</option>
</select>
</div>
</div>
<div class="row mb-2">
<label for="inputFile" class="col-sm-2 col-form-label">Dokument</label>
<div class="col-sm-10">
<span class="error" th:each="error : ${#fields.errors('uploadfile')}">[[${error}]]<br /></span>
<input type="file" id="inputFile" th:field="*{uploadfile}" th:class="${'form-control ' + (#fields.hasErrors('uploadfile') ? 'inputerror' : '')}" />
</div>
</div>
<div class="row mb-2">
<label for="inputFiletype" class="col-sm-2 col-form-label">Dateityp</label>
<div class="col-sm-10">
<span class="error" th:each="error : ${#fields.errors('filetype')}">[[${error}]]<br /></span> <select id="inputFiletype" th:field="*{filetype}"
th:class="${'form-select ' + (#fields.hasErrors('filetype') ? 'inputerror' : '')}">
<option value="">--- bitte wählen ---</option>
<option value="pdf">PDF</option>
<option value="png">PNG</option>
<option value="jpg">JPG</option>
</select>
</div>
</div>
<div class="row mb-2">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<input type="submit" class="btn btn-success" value="Ok" />
<a th:href="@{/admin/document}" class="btn btn-outline-secondary">Abbrechen</a>
</div>
</div>
<!-- TODO:
delete from db th:if="${bean.pk}"
-->
</div>
</div>
</form>
</div>
</th:block>
</body>
</html>

View File

@ -7,7 +7,7 @@
<body> <body>
<th:block layout:fragment="header"> <th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')"> <ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')">
<li class="nav-item"><a th:href="@{/admin}" class="btn btn-seconary btn-icon-silent">Admin</a></li> <li class="nav-item"><a th:href="@{/admin}" class="btn btn-seconary btn-icon-silent">Admin</a></li>

View File

@ -10,15 +10,18 @@
<li class="nav-item"><a th:href="@{/rss/admin}" class="btn btn-seconary btn-icon-silent"><i class="fas fa-rss"></i></a></li> <li class="nav-item"><a th:href="@{/rss/admin}" class="btn btn-seconary btn-icon-silent"><i class="fas fa-rss"></i></a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')"> <ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')">
<li class="nav-item"><a th:href="@{/admin/mail}" class="btn btn-secondary btn-icon-silent">Testmail</a></li> <li class="nav-item"><a th:href="@{/admin/mail}" class="btn btn-secondary btn-icon-silent">Testmail</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0" sec:authorize="hasRole('admin')">
<li class="nav-item"><a th:href="@{/admin/document}" class="btn btn-secondary btn-icon-silent">Dokumente</a></li>
</ul>
</th:block> </th:block>
<th:block layout:fragment="content"> <th:block layout:fragment="content">
<div sec:authorize="hasRole('admin')"> <div sec:authorize="hasRole('admin')">
TODO: implement TODO: implement; list number of camps, registrations and documents or such
</div> </div>
</th:block> </th:block>
</body> </body>

View File

@ -7,7 +7,7 @@
<body> <body>
<th:block layout:fragment="header"> <th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/business/}" class="btn btn-secondary btn-icon-silent">Finanzübersicht</a></li> <li class="nav-item"><a th:href="@{/business/}" class="btn btn-secondary btn-icon-silent">Finanzübersicht</a></li>

View File

@ -7,7 +7,7 @@
<body> <body>
<th:block layout:fragment="header"> <th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/business/}" class="btn btn-secondary btn-icon-silent">Finanzübersicht</a></li> <li class="nav-item"><a th:href="@{/business/}" class="btn btn-secondary btn-icon-silent">Finanzübersicht</a></li>

View File

@ -7,7 +7,7 @@
<body> <body>
<th:block layout:fragment="header"> <th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/business/bookings}" class="btn btn-secondary btn-icon-silent" sec:authorize="hasRole('business_booking')">Buchungsübersicht</a></li> <li class="nav-item"><a th:href="@{/business/bookings}" class="btn btn-secondary btn-icon-silent" sec:authorize="hasRole('business_booking')">Buchungsübersicht</a></li>

View File

@ -7,7 +7,7 @@
<body> <body>
<th:block layout:fragment="header"> <th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/business/}" class="btn btn-secondary btn-icon-silent">Finanzübersicht</a></li> <li class="nav-item"><a th:href="@{/business/}" class="btn btn-secondary btn-icon-silent">Finanzübersicht</a></li>

View File

@ -7,7 +7,7 @@
<body> <body>
<th:block layout:fragment="header"> <th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/business/}" class="btn btn-secondary btn-icon-silent">Finanzübersicht</a></li> <li class="nav-item"><a th:href="@{/business/}" class="btn btn-secondary btn-icon-silent">Finanzübersicht</a></li>

View File

@ -10,7 +10,7 @@
<li class="nav-item"><a th:href="@{/rss/registrator}" class="btn btn-seconary btn-icon-silent"><i class="fas fa-rss"></i></a></li> <li class="nav-item"><a th:href="@{/rss/registrator}" class="btn btn-seconary btn-icon-silent"><i class="fas fa-rss"></i></a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
</th:block> </th:block>
<th:block layout:fragment="content"> <th:block layout:fragment="content">

View File

@ -7,7 +7,7 @@
<body> <body>
<th:block layout:fragment="header"> <th:block layout:fragment="header">
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li> <li class="nav-item"><a th:href="@{/dashboard}" class="btn btn-secondary btn-icon-silent">Hauptseite</a></li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="nav-item"><a th:href="@{/confirmation}" class="btn btn-secondary btn-icon-silent">Anmeldungen</a></li> <li class="nav-item"><a th:href="@{/confirmation}" class="btn btn-secondary btn-icon-silent">Anmeldungen</a></li>

View File

@ -18,10 +18,10 @@
<script th:src="@{/js/mytoggle.js}"></script> <script th:src="@{/js/mytoggle.js}"></script>
<script th:src="@{/js/myAjax.js}"></script> <script th:src="@{/js/myAjax.js}"></script>
<script th:src="@{/js/progress.js}"></script> <script th:src="@{/js/progress.js}"></script>
<th:block layout:fragment="libs"></th:block>
</head> </head>
<body> <body>
<nav class="navbar navbar-expand-lg bg-light"> <nav class="navbar navbar-expand-lg bg-light navbar-background">
<span class="navbar-brand"><img th:src="@{/images/logo.png}" width="128px" /></span>
<div class="container-fluid"> <div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
@ -41,7 +41,7 @@
</div> </div>
</div> </div>
</nav> </nav>
<div layout:fragment="content">Layout content</div> <div layout:fragment="content">content</div>
<div class="versionclip"> <div class="versionclip">
<a href="https://gitlab.com/jottyfan/camporganizer2/-/issues" class="versionlink" target="_blank" th:text="${'Version ' + @manifestBean.getVersion()}"></a> <a href="https://gitlab.com/jottyfan/camporganizer2/-/issues" class="versionlink" target="_blank" th:text="${'Version ' + @manifestBean.getVersion()}"></a>
</div> </div>