Java part of UI for sci-types-department

pull/1/head
Jens Pelzetter 2022-10-01 17:56:28 +02:00
parent 862123fc8d
commit 1c7bef4f98
24 changed files with 2909 additions and 57 deletions

View File

@ -10,7 +10,6 @@ import org.librecms.contenttypes.ContentTypeDescription;
import org.librecms.ui.contentsections.documents.MvcAuthoringKit; import org.librecms.ui.contentsections.documents.MvcAuthoringKit;
import org.scientificcms.contenttypes.scidepartment.ui.SciDepartmentCreateStep; import org.scientificcms.contenttypes.scidepartment.ui.SciDepartmentCreateStep;
import org.scientificcms.contenttypes.scidepartment.ui.SciDepartmentDescriptionStep; import org.scientificcms.contenttypes.scidepartment.ui.SciDepartmentDescriptionStep;
import org.scientificcms.contenttypes.scidepartment.ui.SciDepartmentExtendedPropertiesStep;
import org.scientificcms.contenttypes.scidepartment.ui.SciDepartmentPropertiesStep; import org.scientificcms.contenttypes.scidepartment.ui.SciDepartmentPropertiesStep;
import java.io.Serializable; import java.io.Serializable;
@ -48,7 +47,6 @@ import static org.scientificcms.contenttypes.scidepartment.SciDepartmentConstant
createStep = SciDepartmentCreateStep.class, createStep = SciDepartmentCreateStep.class,
authoringSteps = { authoringSteps = {
SciDepartmentPropertiesStep.class, SciDepartmentPropertiesStep.class,
SciDepartmentExtendedPropertiesStep.class,
SciDepartmentDescriptionStep.class SciDepartmentDescriptionStep.class
} }
) )

View File

@ -0,0 +1,35 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@ApplicationScoped
public class SciDepartmentAuthoringSteps implements MvcAuthoringSteps {
@Override
public Set<Class<?>> getClasses() {
return Set.of(
SciDepartmentPropertiesStep.class,
SciDepartmentDescriptionStep.class
);
}
@Override
public Set<Class<?>> getResourceClasses() {
return Set.of(
SciDepartmentDescriptionContacts.class,
SciDepartmentDescriptionStepResources.class,
SciDepartmentDescriptionStepService.class
);
}
}

View File

@ -0,0 +1,67 @@
package org.scientificcms.contenttypes.scidepartment.ui;
/**
* DTO providing the information about a {@link Contact} of a
* {@link SciDepartment} in a form that is easy to use from a MVC template.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class SciDepartmentContact {
/**
* The ID of the {@link Contact} represented by this object.
*/
private long contactId;
/**
* The type of the contact.
*
* @see Contact#contactType
*/
private String contactType;
/**
* Order of contact.
*/
private long order;
/**
* The title of the {@link ContactableEntity}
*
* @see Contact#contactable
*/
private String contactable;
public long getContactId() {
return contactId;
}
public void setContactId(final long contactId) {
this.contactId = contactId;
}
public String getContactType() {
return contactType;
}
public void setContactType(final String contactType) {
this.contactType = contactType;
}
public long getOrder() {
return order;
}
public void setOrder(final long order) {
this.order = order;
}
public String getContactable() {
return contactable;
}
public void setContactable(final String contactable) {
this.contactable = contactable;
}
}

View File

@ -0,0 +1,67 @@
package org.scientificcms.contenttypes.scidepartment.ui;
/**
* DTO providing the information about a {@link Contact} of a
* {@link SciDepartment} in a form that is easy usable from a MVC template.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class SciDepartmentContactModel {
/**
* The ID of the {@link Contact} represented by this object.
*/
private long contactId;
/**
* The type of the contact.
*
* @see Contact#contactType
*/
private String contactType;
/**
* Order of contact.
*/
private long order;
/**
* The title of the {@link ContactableEntity}
*
* @see Contact#contactable
*/
private String contactable;
public long getContactId() {
return contactId;
}
public void setContactId(final long contactId) {
this.contactId = contactId;
}
public String getContactType() {
return contactType;
}
public void setContactType(final String contactType) {
this.contactType = contactType;
}
public long getOrder() {
return order;
}
public void setOrder(final long order) {
this.order = order;
}
public String getContactable() {
return contactable;
}
public void setContactable(final String contactable) {
this.contactable = contactable;
}
}

View File

@ -1,25 +1,142 @@
package org.scientificcms.contenttypes.scidepartment.ui; package org.scientificcms.contenttypes.scidepartment.ui;
import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentItemRepository;
import org.librecms.ui.contentsections.documents.AbstractMvcDocumentCreateStep; import org.librecms.ui.contentsections.documents.AbstractMvcDocumentCreateStep;
import org.scientificcms.contenttypes.scidepartment.SciDepartment; import org.scientificcms.contenttypes.scidepartment.SciDepartment;
import org.scientificcms.contenttypes.sciproject.SciProject;
import java.util.Map; import java.util.Map;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.transaction.Transactional;
/** /**
* Create step for a {@link SciDepartment}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
@Named("SciDepartmentCreateStep") @Named("SciDepartmentCreateStep")
public class SciDepartmentCreateStep public class SciDepartmentCreateStep
extends AbstractMvcDocumentCreateStep<SciDepartment>{ extends AbstractMvcDocumentCreateStep<SciDepartment> {
private static final String FORM_PARAM_NAME = "name";
private static final String FORM_PARAM_TITLE = "title";
private static final String FORM_PARAM_SHORT_DESCRIPTION = "shortdesc";
private static final String FORM_PARAM_INITIAL_LOCALE = "locale";
private static final String FORM_PARAM_SELECTED_WORKFLOW = "workflow";
/**
* Provides functions for working with content items.
*/
@Inject
private ContentItemManager itemManager;
/**
* Used to save the event.
*/
@Inject
private ContentItemRepository itemRepo;
/**
* Provides functions for working with {@link LocalizedString}s.
*/
@Inject
private GlobalizationHelper globalizationHelper;
/**
* Nmae of the department.
*/
private String name;
/**
* Title of the department.
*/
private String title;
/**
* The short description of the department.
*/
private String shortDescription;
/**
* The initial locale of the department.
*/
private String initialLocale;
/**
* The workflow to use for the new department.
*/
private String selectedWorkflow;
@Override
public String getDocumentType() {
return SciDepartment.class.getName();
}
@Override
public String getDescription() {
return globalizationHelper
.getLocalizedTextsUtil(getBundle())
.getText("createstep.description");
}
@Override
public String getBundle() {
return SciDepartmentStepsConstants.BUNDLE;
}
public String getName() {
return name;
}
public String getTitle() {
return title;
}
public String getShortDescription() {
return shortDescription;
}
public String getInitialLocale() {
return initialLocale;
}
@Transactional(Transactional.TxType.REQUIRED)
public String getSelectedWorkflow() {
if (selectedWorkflow == null || selectedWorkflow.isEmpty()) {
return getContentSection()
.getContentTypes()
.stream()
.filter(
type -> type.getContentItemClass().equals(
SciProject.class.getName()
)
)
.findAny()
.map(type -> type.getDefaultWorkflow())
.map(
workflow -> globalizationHelper.getValueFromLocalizedString(
workflow.getName()
)
)
.orElse("");
} else {
return selectedWorkflow;
}
}
@Override @Override
public String showCreateStep() { public String showCreateStep() {
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody return "org/scientificcms/contenttypes/scidepartment/ui/create-scidepartment.xhtml";
} }
@Override @Override
@ -27,19 +144,4 @@ public class SciDepartmentCreateStep
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
} }
@Override
public String getDocumentType() {
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
}
@Override
public String getDescription() {
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
}
@Override
public String getBundle() {
throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
}
} }

View File

@ -0,0 +1,101 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.ui.contentsections.ContentSectionsUi;
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
import org.scientificcms.contenttypes.scidepartment.Contact;
import org.scientificcms.contenttypes.scidepartment.ContactRepository;
import org.scientificcms.contenttypes.scidepartment.SciDepartment;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path(MvcAuthoringSteps.PATH_PREFIX + "scidepartment-description-contacts")
public class SciDepartmentDescriptionContacts {
@Inject
private ContactRepository contactRepo;
@Inject
private ContentItemRepository itemRepo;
@Inject
private ContentSectionsUi sectionsUi;
@POST
@Path("/save-order")
@Consumes(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
public Response saveOrder(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM)
final String documentPath,
final List<String> order
) {
final ContentSection contentSection = sectionsUi
.findContentSection(sectionIdentifier)
.orElseThrow(
() -> new NotFoundException(
String.format(
"No content identifed by %s found.",
sectionIdentifier
)
)
);
final ContentItem document = itemRepo
.findByPath(contentSection, documentPath)
.orElseThrow(
() -> new NotFoundException(
String.format(
"No document for path %s in section %s.",
documentPath,
contentSection.getLabel()
)
)
);
if (!(document instanceof SciDepartment)) {
throw new NotFoundException(
String.format(
"No SciDepartment for path %s in section %s.",
documentPath,
contentSection.getLabel()
)
);
}
final Map<Long, Long> orderMap = new HashMap<>();
for (int i = 0; i < order.size(); i++) {
orderMap.put(Long.parseLong(order.get(i)), (long) i);
}
final SciDepartment department = (SciDepartment)document;
for (final Contact contact : department.getContacts()) {
contact.setOrder(orderMap.get(contact.getContactId()));
contactRepo.save(contact);
}
return Response.ok().build();
}
}

View File

@ -0,0 +1,57 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.librecms.assets.Organization;
import org.librecms.assets.Person;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("SciDepartmentDescriptionContacts")
public class SciDepartmentDescriptionContactsModel {
private boolean canEdit;
private List<SciDepartmentContactModel> contacts;
private String baseUrl;
public boolean getCanEdit() {
return canEdit;
}
protected void setCanEdit(final boolean canEdit) {
this.canEdit = canEdit;
}
public List<SciDepartmentContactModel> getContacts() {
return Collections.unmodifiableList(contacts);
}
protected void setContacts(final List<SciDepartmentContactModel> contacts) {
this.contacts = new ArrayList<>(contacts);
}
public String getContactableTypes() {
return String.join(
",", Person.class.getName(), Organization.class.getName()
);
}
public String getBaseUrl() {
return baseUrl;
}
protected void setBaseUrl(final String baseUrl) {
this.baseUrl = baseUrl;
}
}

View File

@ -0,0 +1,60 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("SciDepartmentDescriptionMembers")
public class SciDepartmentDescriptionMembersModel {
private boolean canEdit;
private List<SciDepartmentMembershipModel> members;
private Map<String, String> statusValues;
private String baseUrl;
public boolean getCanEdit() {
return canEdit;
}
protected void setCanEdit(final boolean canEdit) {
this.canEdit = canEdit;
}
public List<SciDepartmentMembershipModel> getMembers() {
return Collections.unmodifiableList(members);
}
protected void setMembers(final List<SciDepartmentMembershipModel> member) {
this.members = new ArrayList<>(members);
}
public Map<String, String> getStatusValues() {
return Collections.unmodifiableMap(statusValues);
}
protected void setStatusValues(final Map<String, String> statusValues) {
this.statusValues = new LinkedHashMap<>(statusValues);
}
public String getBaseUrl() {
return baseUrl;
}
protected void setBaseUrl(final String baseUrl) {
this.baseUrl = baseUrl;
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright (C) 2022 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.scientificcms.contenttypes.scidepartment.ui;
import org.librecms.ui.contentsections.documents.CmsEditorLocaleVariantRow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("SciDepartmentDescriptionModel")
public class SciDepartmentDescriptionModel {
private boolean canEdit;
private Map<String, String> descriptionValues;
private List<CmsEditorLocaleVariantRow> variants;
private List<String> unusedLocales;
private String selectedLocale;
public Map<String, String> getDescriptionValues() {
return Optional
.ofNullable(descriptionValues)
.map(Collections::unmodifiableMap)
.orElse(Collections.emptyMap());
}
protected void setDescriptionValues(
final Map<String, String> descriptionValues
) {
this.descriptionValues = Optional
.ofNullable(descriptionValues)
.map(values -> new HashMap<>(values))
.map(values -> (Map<String, String>) values)
.orElse(Collections.emptyMap());
}
public List<CmsEditorLocaleVariantRow> getVariants() {
return Optional
.ofNullable(variants)
.map(Collections::unmodifiableList)
.orElse(Collections.emptyList());
}
protected void setVariants(final List<CmsEditorLocaleVariantRow> variants) {
this.variants = Optional
.ofNullable(variants)
.map(list -> new ArrayList<>(list))
.map(list -> (List<CmsEditorLocaleVariantRow>) list)
.orElse(Collections.emptyList());
}
public List<String> getUnusedLocales() {
return Optional
.ofNullable(unusedLocales)
.map(Collections::unmodifiableList)
.orElse(Collections.emptyList());
}
protected void setUnusedLocales(final List<String> unusedLocales) {
this.unusedLocales = Optional
.ofNullable(unusedLocales)
.map(list -> new ArrayList<>(list))
.map(list -> (List<String>) list)
.orElse(Collections.emptyList());
}
public String getSelectedLocale() {
return selectedLocale;
}
protected void setSelectedLocale(final String selectedLocale) {
this.selectedLocale = selectedLocale;
}
public boolean getCanEdit() {
return canEdit;
}
protected void setCanEdit(final boolean canEdit) {
this.canEdit = canEdit;
}
}

View File

@ -0,0 +1,123 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.ui.contentsections.ContentSectionsUi;
import org.librecms.ui.contentsections.ItemPermissionChecker;
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
import org.scientificcms.contenttypes.scidepartment.SciDepartment;
import java.util.Locale;
import java.util.StringTokenizer;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path(MvcAuthoringSteps.PATH_PREFIX + "scidepartment-description-resources")
public class SciDepartmentDescriptionStepResources {
@Inject
private ContentItemRepository itemRepo;
@Inject
private ContentSectionsUi sectionsUi;
@Inject
private ItemPermissionChecker itemPermissionChecker;
@GET
@Path("/department-description/wordcount/{locale}")
@Produces(MediaType.TEXT_HTML)
@Transactional(Transactional.TxType.REQUIRED)
public String getDepartmentDescriptionWordCount(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPathParam,
@PathParam("locale") final String localeParam
) {
final ContentSection contentSection = sectionsUi
.findContentSection(sectionIdentifier)
.orElseThrow(
() -> new NotFoundException()
);
final ContentItem document = itemRepo
.findByPath(contentSection, documentPathParam)
.orElseThrow(
() -> new NotFoundException()
);
if (!(document instanceof SciDepartment)) {
throw new NotFoundException();
}
final SciDepartment department = (SciDepartment) document;
if (itemPermissionChecker.canEditItem(department)) {
final String text = department
.getDepartmentDescription()
.getValue(new Locale(localeParam));
final Document jsoupDoc = Jsoup.parseBodyFragment(text);
final long result = new StringTokenizer(
jsoupDoc.body().text()
).countTokens();
return Long.toString(result);
} else {
throw new ForbiddenException();
}
}
@GET
@Path("/department-description/{locale}")
@Produces(MediaType.TEXT_HTML)
@Transactional(Transactional.TxType.REQUIRED)
public String viewDepartmentDescriptionValue(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPathParam,
@PathParam("locale") final String localeParam
) {
final ContentSection contentSection = sectionsUi
.findContentSection(sectionIdentifier)
.orElseThrow(
() -> new NotFoundException()
);
final ContentItem document = itemRepo
.findByPath(contentSection, documentPathParam)
.orElseThrow(
() -> new NotFoundException()
);
if (!(document instanceof SciDepartment)) {
throw new NotFoundException();
}
final SciDepartment department = (SciDepartment) document;
if (itemPermissionChecker.canEditItem(department)) {
return department.getDepartmentDescription().getValue(
new Locale(localeParam)
);
} else {
throw new ForbiddenException();
}
}
}

View File

@ -0,0 +1,130 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.ui.contentsections.ContentSectionsUi;
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
import org.scientificcms.contenttypes.scidepartment.Contact;
import org.scientificcms.contenttypes.scidepartment.ContactRepository;
import org.scientificcms.contenttypes.scidepartment.SciDepartment;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path(MvcAuthoringSteps.PATH_PREFIX + "scidepartment-description-service")
public class SciDepartmentDescriptionStepService {
@Inject
private ContactRepository contactRepo;
@Inject
private ContentItemRepository itemRepo;
@Inject
private ContentSectionsUi sectionsUi;
@POST
@Path("/contacts/save-order")
@Consumes(MediaType.APPLICATION_JSON)
@Transactional(Transactional.TxType.REQUIRED)
public Response saveContactsOrder(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
final List<String> contactsOrder
) {
final ContentSection contentSection = sectionsUi
.findContentSection(sectionIdentifier)
.orElseThrow(
() -> new NotFoundException(
String.format(
"No content identifed by %s found.",
sectionIdentifier
)
)
);
final ContentItem document = itemRepo
.findByPath(contentSection, documentPath)
.orElseThrow(
() -> new NotFoundException(
String.format(
"No document for path %s in section %s.",
documentPath,
contentSection.getLabel()
)
)
);
if (!(document instanceof SciDepartment)) {
throw new BadRequestException(
String.format(
"Document %s is not a %s.",
documentPath,
SciDepartment.class.getSimpleName()
)
);
}
final SciDepartment department = (SciDepartment) document;
final List<Contact> contacts = department.getContacts();
if (contactsOrder.size() != contacts.size()) {
throw new BadRequestException(
String.format(
"Number of contacts of the SciDepartment %s does "
+ "not match the number of values in the order "
+ "list. Number of contactas: %d, number of values in "
+ "the contacts order list: %d",
documentPath,
contacts.size(),
contactsOrder.size()
)
);
}
for (int i = 0; i < contactsOrder.size(); i++) {
final String contactIdParam = contactsOrder.get(i);
final long contactId = Long.parseLong(contactIdParam);
final Contact contact = contacts
.stream()
.filter(con -> con.getContactId() == contactId)
.findAny()
.orElseThrow(
() -> new BadRequestException(
String.format(
"contactsOrder has an entry for contact with "
+ "ID %d, but there is not contact with that "
+ "ID.",
contactId
)
)
);
contact.setOrder(i);
contactRepo.save(contact);
}
return Response.ok().build();
}
}

View File

@ -1,26 +0,0 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.librecms.ui.contentsections.documents.MvcAuthoringStepDef;
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
import org.scientificcms.contenttypes.scidepartment.SciDepartment;
import javax.enterprise.context.RequestScoped;
import javax.mvc.Controller;
import javax.ws.rs.Path;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path(MvcAuthoringSteps.PATH_PREFIX + "scidepartment-extendedproperties")
@Controller
@MvcAuthoringStepDef(
bundle = SciDepartmentStepsConstants.BUNDLE,
descriptionKey = "authoringsteps.extendedproperties.description",
labelKey = "authoringsteps.extendedproperties.label",
supportedDocumentType = SciDepartment.class
)
public class SciDepartmentExtendedPropertiesStep {
}

View File

@ -0,0 +1,143 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.scientificcms.contenttypes.scidepartment.Membership;
import org.scientificcms.contenttypes.scidepartment.SciDepartment;
/**
* DTO providing the information about a {@link Membership} of a
* {@link SciDepartment} in an form that easy usable from a MVC template.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class SciDepartmentMembershipModel {
/**
* The ID of the {@link Membership}.
*/
private long membershipId;
/**
* The role of the member.
*
* @see Membership#role
*/
private String role;
/**
* The status of the membership.
*
* @see Membership#status
*/
private String status;
/**
* The given name of the member.
*
* @see PersonName#givenName
*/
private String givenName;
/**
* The surname of the member.
*
* @see PersonName#surname
*/
private String surname;
/**
* An optional prefix for the name of the member.
*
* @see PersonName#prefix
*/
private String prefix;
/**
* An optional suffix for the name of the member.
*
* @see PersonName#suffix
*/
private String suffix;
public long getMembershipId() {
return membershipId;
}
public void setMembershipId(final long membershipId) {
this.membershipId = membershipId;
}
public String getRole() {
return role;
}
public void setRole(final String role) {
this.role = role;
}
public String getStatus() {
return status;
}
public void setStatus(final String status) {
this.status = status;
}
public String getName() {
final StringBuilder builder = new StringBuilder();
if (prefix != null && !prefix.isBlank()) {
builder.append(prefix);
builder.append(" ");
}
if (surname != null && !surname.isBlank()) {
builder.append(surname);
}
if (surname != null
&& !surname.isBlank()
&& givenName != null
&& !givenName.isBlank()) {
builder.append(", ");
}
if (givenName != null && !givenName.isBlank()) {
builder.append(givenName);
}
if (suffix != null && !suffix.isBlank()) {
builder.append(" ");
builder.append(suffix);
}
return builder.toString();
}
public String getGivenName() {
return givenName;
}
public void setGivenName(final String givenName) {
this.givenName = givenName;
}
public String getSurname() {
return surname;
}
public void setSurname(final String surname) {
this.surname = surname;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(final String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(final String suffix) {
this.suffix = suffix;
}
}

View File

@ -0,0 +1,21 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.libreccm.ui.AbstractMessagesBean;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("SciDepartmentMessageBundle")
public class SciDepartmentMessageBundle extends AbstractMessagesBean {
@Override
protected String getMessageBundle() {
return SciDepartmentStepsConstants.BUNDLE;
}
}

View File

@ -1,12 +1,33 @@
package org.scientificcms.contenttypes.scidepartment.ui; package org.scientificcms.contenttypes.scidepartment.ui;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.l10n.LocalizedString;
import org.libreccm.security.AuthorizationRequired;
import org.librecms.contentsection.ContentItemRepository;
import org.librecms.ui.contentsections.ContentSectionNotFoundException;
import org.librecms.ui.contentsections.ItemPermissionChecker;
import org.librecms.ui.contentsections.documents.AbstractMvcAuthoringStep;
import org.librecms.ui.contentsections.documents.DocumentNotFoundException;
import org.librecms.ui.contentsections.documents.DocumentUi;
import org.librecms.ui.contentsections.documents.MvcAuthoringStepDef; import org.librecms.ui.contentsections.documents.MvcAuthoringStepDef;
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
import org.scientificcms.contenttypes.scidepartment.SciDepartment; import org.scientificcms.contenttypes.scidepartment.SciDepartment;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.mvc.Controller; import javax.mvc.Controller;
import javax.mvc.Models;
import javax.transaction.Transactional;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
/** /**
* *
@ -21,6 +42,451 @@ import javax.ws.rs.Path;
labelKey = "authoringsteps.basicproperties.label", labelKey = "authoringsteps.basicproperties.label",
supportedDocumentType = SciDepartment.class supportedDocumentType = SciDepartment.class
) )
public class SciDepartmentPropertiesStep { public class SciDepartmentPropertiesStep extends AbstractMvcAuthoringStep {
@Inject
private SciDepartmentMessageBundle messageBundle;
/**
* Used for retrieving and saving the departments.
*/
@Inject
private ContentItemRepository itemRepo;
@Inject
private DocumentUi documentUi;
/**
* Provides functions for working with {@link LocalizedString}s.
*/
@Inject
private GlobalizationHelper globalizationHelper;
@Inject
private ItemPermissionChecker itemPermissionChecker;
@Inject
private SciDepartmentPropertiesStepModel propertiesStepModel;
@Inject
private Models models;
@Override
public Class<SciDepartmentPropertiesStep> getStepClass() {
return SciDepartmentPropertiesStep.class;
}
@Override
@Transactional(Transactional.TxType.REQUIRED)
protected void init() throws ContentSectionNotFoundException,
DocumentNotFoundException {
super.init();
propertiesStepModel.setName(getDocument().getDisplayName());
final Set<Locale> titleLocales = getDocument()
.getTitle()
.getAvailableLocales();
propertiesStepModel.setTitleValues(
getDocument()
.getTitle()
.getValues()
.entrySet()
.stream()
.collect(
Collectors.toMap(
entry -> entry.getKey().toString(),
entry -> entry.getValue()
)
)
);
propertiesStepModel.setUnusedTitleLocales(
globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !titleLocales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList())
);
final SciDepartment department = (SciDepartment) getDocument();
propertiesStepModel.setShortDescriptionValues(
department
.getShortDescription()
.getValues()
.entrySet()
.stream()
.collect(
Collectors.toMap(
entry -> entry.getKey().toString(),
entry -> entry.getValue()
)
)
);
final Set<Locale> shortDescriptionLocales = department
.getShortDescription()
.getAvailableLocales();
propertiesStepModel.setUnusedShortDescriptionLocales(
globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !shortDescriptionLocales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList())
);
}
@GET
@Path("/")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
public String showStep(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getDocument())) {
return "org/scientificcms/contenttypes/scidepartment/ui/scidepartment-basic-properties.xhtml";
} else {
return documentUi.showAccessDenied(
getContentSection(),
getDocument(),
messageBundle.getMessage("scidepartment.edit.denied")
);
}
}
/**
* Updates the name of the current department.
*
* @param sectionIdentifier
* @param documentPath
* @param name
*
* @return A redirect to this authoring step.
*/
@POST
@Path("/name")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String updateName(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@FormParam("name") @DefaultValue("") final String name
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getDocument())) {
if (name.isBlank()) {
models.put("nameMissing", true);
return showStep(sectionIdentifier, documentPath);
}
getDocument().setDisplayName(name);
itemRepo.save(getDocument());
updateDocumentPath();
return buildRedirectPathForStep();
} else {
return documentUi.showAccessDenied(
getContentSection(),
getDocument(),
getLabel()
);
}
}
/**
* Updates a localized title of the department.
*
* @param sectionIdentifier
* @param documentPath
* @param localeParam The locale to update.
* @param value The updated title value.
*
* @return A redirect to this authoring step.
*/
@POST
@Path("/title/@add")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String addTitle(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@FormParam("locale") final String localeParam,
@FormParam("value") final String value
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getDocument())) {
final Locale locale = new Locale(localeParam);
getDocument().getTitle().putValue(locale, value);
itemRepo.save(getDocument());
return buildRedirectPathForStep();
} else {
return documentUi.showAccessDenied(
getContentSection(),
getDocument(),
getLabel()
);
}
}
/**
* Updates a localized title of the department.
*
* @param sectionIdentifier
* @param documentPath
* @param localeParam The locale to update.
* @param value The updated title value.
*
* @return A redirect to this authoring step.
*/
@POST
@Path("/title/@edit/{locale}")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String editTitle(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@PathParam("locale") final String localeParam,
@FormParam("value") final String value
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getDocument())) {
final Locale locale = new Locale(localeParam);
getDocument().getTitle().putValue(locale, value);
itemRepo.save(getDocument());
return buildRedirectPathForStep();
} else {
return documentUi.showAccessDenied(
getContentSection(),
getDocument(),
getLabel()
);
}
}
/**
* Removes a localized title of the department.
*
* @param sectionIdentifier
* @param documentPath
* @param localeParam The locale to remove.
*
* @return A redirect to this authoring step.
*/
@POST
@Path("/title/@remove/{locale}")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String removeTitle(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@PathParam("locale") final String localeParam
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getDocument())) {
final Locale locale = new Locale(localeParam);
getDocument().getTitle().removeValue(locale);
itemRepo.save(getDocument());
return buildRedirectPathForStep();
} else {
return documentUi.showAccessDenied(
getContentSection(),
getDocument(),
getLabel()
);
}
}
/**
* Adds a localized short description to the department.
*
* @param sectionIdentifier
* @param documentPath
* @param localeParam The locale of the description.
* @param value The description value.
*
* @return A redirect to this authoring step.
*/
@POST
@Path("/short-description/@add")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
public String addShortDescription(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@FormParam("locale") final String localeParam,
@FormParam("value") final String value
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getDocument())) {
final Locale locale = new Locale(localeParam);
final SciDepartment department = (SciDepartment) getDocument();
department.getShortDescription().putValue(locale, value);
itemRepo.save(department);
return buildRedirectPathForStep();
} else {
return documentUi.showAccessDenied(
getContentSection(),
getDocument(),
getLabel()
);
}
}
/**
* Updates a localized short description to the department.
*
* @param sectionIdentifier
* @param documentPath
* @param localeParam The locale to update.
* @param value The description value.
*
* @return A redirect to this authoring step.
*/
@POST
@Path("/short-description/@edit/{locale}")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
public String editShortDescription(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@PathParam("locale") final String localeParam,
@FormParam("value") final String value
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getDocument())) {
final Locale locale = new Locale(localeParam);
final SciDepartment department = (SciDepartment) getDocument();
department.getShortDescription().putValue(locale, value);
itemRepo.save(department);
return buildRedirectPathForStep();
} else {
return documentUi.showAccessDenied(
getContentSection(),
getDocument(),
getLabel()
);
}
}
/**
* Removes a localized short description from the department.
*
* @param sectionIdentifier
* @param documentPath
* @param localeParam The locale to remove.
*
* @return A redirect to this authoring step.
*/
@POST
@Path("/short-description/@remove/{locale}")
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
public String removeShortDescription(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@PathParam("locale") final String localeParam
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getDocument())) {
final Locale locale = new Locale(localeParam);
final SciDepartment department = (SciDepartment) getDocument();
department.getShortDescription().removeValue(locale);
itemRepo.save(department);
return buildRedirectPathForStep();
} else {
return documentUi.showAccessDenied(
getContentSection(),
getDocument(),
getLabel()
);
}
}
} }

View File

@ -0,0 +1,76 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Named("SciDepartmentPropertiesStep")
public class SciDepartmentPropertiesStepModel {
private String name;
private Map<String, String> titleValues;
private List<String> unusedTitleLocales;
private Map<String, String> shortDescriptionValues;
private List<String> unusedShortDescriptionLocales;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public Map<String, String> getTitleValues() {
return Collections.unmodifiableMap(titleValues);
}
public void setTitleValues(final Map<String, String> titleValues) {
this.titleValues = new HashMap<>(titleValues);
}
public List<String> getUnusedTitleLocales() {
return Collections.unmodifiableList(unusedTitleLocales);
}
public void setUnusedTitleLocales(final List<String> unusedTitleLocales) {
this.unusedTitleLocales = new ArrayList<>(unusedTitleLocales);
}
public Map<String, String> getShortDescriptionValues() {
return Collections.unmodifiableMap(shortDescriptionValues);
}
public void setShortDescriptionValues(
final Map<String, String> shortDescriptionValues
) {
this.shortDescriptionValues = new HashMap<>(shortDescriptionValues);
}
public List<String> getUnusedShortDescriptionLocales() {
return Collections.unmodifiableList(unusedShortDescriptionLocales);
}
public void setUnusedShortDescriptionLocales(
final List<String> unusedShortDescriptionLocales
) {
this.unusedShortDescriptionLocales = new ArrayList<>(
unusedShortDescriptionLocales
);
}
}

View File

@ -0,0 +1,84 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import org.librecms.ui.contentsections.documents.CmsEditorLocaleVariantRow;
import org.scientificcms.contenttypes.scidepartment.DepartmentText;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* DTO providing the data of a {@link DepartmentText} in a form that is easy
* usable from the view.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class SciDepartmentTextModel {
private Map<String, String> textValues;
private List<CmsEditorLocaleVariantRow> variants;
private List<String> unusedLocales;
private long order;
public Map<String, String> getTextValues() {
return Optional
.ofNullable(textValues)
.map(Collections::unmodifiableMap)
.orElse(Collections.emptyMap());
}
protected void setTextValues(
final Map<String, String> textValues
) {
this.textValues = Optional
.ofNullable(textValues)
.map(values -> new HashMap<>(values))
.map(values -> (Map<String, String>) values)
.orElse(Collections.emptyMap());
}
public List<CmsEditorLocaleVariantRow> getVariants() {
return Optional
.ofNullable(variants)
.map(Collections::unmodifiableList)
.orElse(Collections.emptyList());
}
protected void setVariants(final List<CmsEditorLocaleVariantRow> variants) {
this.variants = Optional
.ofNullable(variants)
.map(list -> new ArrayList<>(list))
.map(list -> (List<CmsEditorLocaleVariantRow>) list)
.orElse(Collections.emptyList());
}
public List<String> getUnusedLocales() {
return Optional
.ofNullable(unusedLocales)
.map(Collections::unmodifiableList)
.orElse(Collections.emptyList());
}
protected void setUnusedLocales(final List<String> unusedLocales) {
this.unusedLocales = Optional
.ofNullable(unusedLocales)
.map(list -> new ArrayList<>(list))
.map(list -> (List<String>) list)
.orElse(Collections.emptyList());
}
public long getOrder() {
return order;
}
public void setOrder(final long order) {
this.order = order;
}
}

View File

@ -0,0 +1,53 @@
package org.scientificcms.contenttypes.scidepartment.ui;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class SciDepartmentTextsModel {
private boolean canEdit;
private Map<String, SciDepartmentTextModel> texts;
private String selectedText;
private String selectedLocale;
public boolean getCanEdit() {
return canEdit;
}
protected void setCanEdit(final boolean canEdit) {
this.canEdit = canEdit;
}
public Map<String, SciDepartmentTextModel> getTexts() {
return Collections.unmodifiableMap(texts);
}
protected void setTexts(final Map<String, SciDepartmentTextModel> texts) {
this.texts = new HashMap<>(texts);
}
public String getSelectedText() {
return selectedText;
}
public void setSelectedText(final String selectedText) {
this.selectedText = selectedText;
}
public String getSelectedLocale() {
return selectedLocale;
}
protected void setSelectedLocale(final String selectedLocale) {
this.selectedLocale = selectedLocale;
}
}

View File

@ -0,0 +1,7 @@
createstep.description=Creates a new SciDepartment.
authoringsteps.description.description=Description of the department.
authoringsteps.description.label=Description
scidepartment.edit.denied=Access denied.
authoringsteps.basicproperties.description=Basicproperties of the department.
authoringsteps.basicproperties.label=Basic Properties

View File

@ -0,0 +1,7 @@
createstep.description=Erstellt eine neue Abteilung.
authoringsteps.description.description=Beschreibung der Abteilung.
authoringsteps.description.label=Beschreibung
scidepartment.edit.denied=Zugriff verweigert.
authoringsteps.basicproperties.description=Basiseigenschaften der Abteilung.
authoringsteps.basicproperties.label=Basiseigenschaften

View File

@ -76,7 +76,7 @@ public class SciProjectDescriptionStepService {
) )
); );
if ((document instanceof SciProject)) { if (!(document instanceof SciProject)) {
throw new BadRequestException( throw new BadRequestException(
String.format( String.format(
"Document %s is not a %s.", "Document %s is not a %s.",

View File

@ -0,0 +1 @@
runtime.properties

View File

@ -1,7 +0,0 @@
libreccm.debug.suspend=n
libreccm.database.host=localhost
libreccm.database.port=5432
libreccm.database.name=scicms-devel
libreccm.database.user=ccm
libreccm.database.password=ccm47web