Coverage Summary for Class: ProjectForm (org.kitodo.production.forms)
Class |
Class, %
|
Method, %
|
Line, %
|
ProjectForm |
0%
(0/1)
|
0%
(0/55)
|
0%
(0/249)
|
/*
* (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/
package org.kitodo.production.forms;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Locale.LanguageRange;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.inject.Named;
import javax.xml.bind.JAXBException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kitodo.config.xml.fileformats.FileFormat;
import org.kitodo.config.xml.fileformats.FileFormatsConfig;
import org.kitodo.data.database.beans.Folder;
import org.kitodo.data.database.beans.ImportConfiguration;
import org.kitodo.data.database.beans.Project;
import org.kitodo.data.database.beans.Template;
import org.kitodo.data.database.beans.User;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.data.exceptions.DataException;
import org.kitodo.exceptions.ProjectDeletionException;
import org.kitodo.forms.FolderGenerator;
import org.kitodo.production.controller.SecurityAccessController;
import org.kitodo.production.dto.ProjectDTO;
import org.kitodo.production.dto.TemplateDTO;
import org.kitodo.production.enums.ObjectType;
import org.kitodo.production.helper.Helper;
import org.kitodo.production.model.LazyDTOModel;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.services.data.ProjectService;
@Named("ProjectForm")
@SessionScoped
public class ProjectForm extends BaseForm {
public static final String MIMETYPE_PREFIX_AUDIO = "audio";
private static final Logger logger = LogManager.getLogger(ProjectForm.class);
public static final String MIMETYPE_PREFIX_VIDEO = "video";
private Project project;
private List<Template> deletedTemplates = new ArrayList<>();
private boolean locked = true;
private static final String TITLE_USED = "projectTitleAlreadyInUse";
private Boolean hasProcesses;
/**
* An encapsulation of the content generator properties of the folder in a
* way suitable to the JSF design.
*/
private FolderGenerator generator = new FolderGenerator(this.myFolder);
/**
* Initialize the list of displayed list columns.
*/
@PostConstruct
public void init() {
// Lists of available list columns
columns = new ArrayList<>();
try {
columns.add(ServiceManager.getListColumnService().getListColumnsForListAsSelectItemGroup("project"));
columns.add(ServiceManager.getListColumnService().getListColumnsForListAsSelectItemGroup("template"));
columns.add(ServiceManager.getListColumnService().getListColumnsForListAsSelectItemGroup("workflow"));
columns.add(ServiceManager.getListColumnService().getListColumnsForListAsSelectItemGroup("docket"));
columns.add(ServiceManager.getListColumnService().getListColumnsForListAsSelectItemGroup("ruleset"));
} catch (DAOException e) {
Helper.setErrorMessage(e.getLocalizedMessage(), logger, e);
}
// Lists of selected list columns
selectedColumns = new ArrayList<>();
selectedColumns.addAll(ServiceManager.getListColumnService().getSelectedListColumnsForListAndClient("project"));
selectedColumns
.addAll(ServiceManager.getListColumnService().getSelectedListColumnsForListAndClient("template"));
selectedColumns
.addAll(ServiceManager.getListColumnService().getSelectedListColumnsForListAndClient("workflow"));
selectedColumns.addAll(ServiceManager.getListColumnService().getSelectedListColumnsForListAndClient("docket"));
selectedColumns.addAll(ServiceManager.getListColumnService().getSelectedListColumnsForListAndClient("ruleset"));
}
/**
* The folder currently under edit in the pop-up dialog.
*/
/*
* This is a hack. The clean solution would be to have an inner class bean
* for the data table row a dialog, but this approach was introduced
* decades ago and has been maintained until today.
*/
private Folder myFolder;
private Project baseProject;
// lists accepting the preliminary actions of adding and deleting folders
// it needs the execution of commit folders to make these changes
// permanent
private List<Integer> newFolders = new ArrayList<>();
private List<Integer> deletedFolders = new ArrayList<>();
private boolean copyTemplates;
private final String projectEditPath = MessageFormat.format(REDIRECT_PATH, "projectEdit");
private String projectEditReferer = DEFAULT_LINK;
/**
* Cash for the list of possible MIME types. So that the list does not have
* to be read from file several times for one page load.
*/
private Map<String, String> mimeTypes = Collections.emptyMap();
/**
* Empty default constructor that also sets the LazyDTOModel instance of
* this bean.
*/
public ProjectForm() {
super();
super.setLazyDTOModel(new LazyDTOModel(ServiceManager.getProjectService()));
}
/**
* This method deletes folders by their IDs in the list.
*
* @param folderIds
* IDs of folders to delete
*/
private void removeFoldersFromProject(List<Integer> folderIds) {
if (Objects.nonNull(project)) {
for (Integer id : folderIds) {
for (Folder f : project.getFolders()) {
if (Objects.isNull(f.getId()) ? Objects.isNull(id) : f.getId().equals(id)) {
project.getFolders().remove(f);
break;
}
}
}
}
}
/**
* this method flushes the newFolders list, thus makes them permanent and
* deletes those marked for deleting, making the removal permanent.
*/
private void commitFolders() throws DAOException {
// resetting the list of new folders
newFolders = new ArrayList<>();
// deleting the folders marked for deletion
removeFoldersFromProject(deletedFolders);
// resetting the list of folders marked for deletion
deletedFolders = new ArrayList<>();
}
/**
* This needs to be executed in order to rollback adding of folders.
*/
public void cancel() {
// flushing new folders
removeFoldersFromProject(newFolders);
// resetting the list of new folders
newFolders = new ArrayList<>();
// resetting the List of folders marked for deletion
deletedFolders = new ArrayList<>();
}
/**
* Create new project.
*
* @return page address
*/
public String newProject() {
this.project = new Project();
this.locked = false;
this.project.setClient(ServiceManager.getUserService().getSessionClientOfAuthenticatedUser());
return projectEditPath;
}
/**
* Duplicate the selected project.
*
* @param itemId
* ID of the project to duplicate
* @return page address; either redirect to the edit project page or return
* 'null' if the project could not be retrieved, which will prompt
* JSF to remain on the same page and reuse the bean.
*/
public String duplicate(Integer itemId) {
setCopyTemplates(true);
this.locked = false;
try {
this.baseProject = ServiceManager.getProjectService().getById(itemId);
this.project = ServiceManager.getProjectService().duplicateProject(baseProject);
this.setSaveDisabled(false);
return projectEditPath + "&referer=projects";
} catch (DAOException e) {
Helper.setErrorMessage(ERROR_DUPLICATE, new Object[] {ObjectType.PROJECT.getTranslationSingular() }, logger,
e);
return this.stayOnCurrentPage;
}
}
/**
* Saves current project if title is not empty and redirects to projects
* page.
*
* @return page or null
*/
public String save() {
ServiceManager.getProjectService().evict(project);
if (isTitleValid()) {
try {
addFirstUserToNewProject();
commitTemplates();
commitFolders();
ServiceManager.getProjectService().save(project, true);
return projectsPage;
} catch (DAOException | DataException e) {
Helper.setErrorMessage(ERROR_SAVING, new Object[] {ObjectType.PROJECT.getTranslationSingular() },
logger, e);
return this.stayOnCurrentPage;
}
} else {
return this.stayOnCurrentPage;
}
}
private void commitTemplates() throws DAOException {
if (copyTemplates) {
for (Template template : baseProject.getTemplates()) {
template.getProjects().add(project);
project.getTemplates().add(template);
}
setCopyTemplates(false);
}
for (Template template : project.getTemplates()) {
ServiceManager.getTemplateService().saveToDatabase(template);
}
for (Template template : deletedTemplates) {
ServiceManager.getTemplateService().saveToDatabase(template);
}
deletedTemplates = new ArrayList<>();
}
private boolean isTitleValid() {
String projectTitle = this.project.getTitle();
if (StringUtils.isNotBlank(projectTitle)) {
List<Project> projects = ServiceManager.getProjectService().getProjectsWithTitleAndClient(projectTitle,
this.project.getClient().getId());
int count = projects.size();
if (count > 1) {
Helper.setErrorMessage(ERROR_OCCURRED, TITLE_USED);
return false;
} else if (count == 1) {
Integer projectId = this.project.getId();
if (Objects.nonNull(projectId) && projects.get(0).getId().equals(projectId)) {
return true;
}
Helper.setErrorMessage(ERROR_OCCURRED, TITLE_USED);
return false;
}
return true;
}
Helper.setErrorMessage(ERROR_INCOMPLETE_DATA, "errorProjectNoTitleGiven");
return false;
}
private void addFirstUserToNewProject() throws DAOException {
if (this.project.getUsers().isEmpty()) {
User user = ServiceManager.getUserService().getCurrentUser();
user.getProjects().add(this.project);
this.project.getUsers().add(user);
ServiceManager.getProjectService().saveToDatabase(this.project);
ServiceManager.getUserService().saveToDatabase(user);
}
}
/**
* Remove.
*/
public void delete(int projectId) {
try {
ProjectService.delete(projectId);
} catch (DAOException | DataException e) {
Helper.setErrorMessage(ERROR_DELETING, new Object[] {ObjectType.PROJECT.getTranslationSingular() }, logger,
e);
} catch (ProjectDeletionException e) {
Helper.setErrorMessage(e.getMessage());
}
}
/**
* Add folder.
*
* @return String
*/
public String addFolder() {
this.myFolder = new Folder();
this.myFolder.setProject(this.project);
this.generator = new FolderGenerator(myFolder);
this.newFolders.add(this.myFolder.getId());
return this.stayOnCurrentPage;
}
/**
* Save folder.
*/
public void saveFolder() {
if (!this.project.getFolders().contains(this.myFolder)) {
this.project.getFolders().add(this.myFolder);
try {
ServiceManager.getProjectService().saveToDatabase(this.project);
} catch (DAOException e) {
Helper.setErrorMessage(ERROR_SAVING, new Object[] {ObjectType.PROJECT.getTranslationSingular() },
logger, e);
}
} else {
List<Folder> folders = this.project.getFolders();
for (Folder folder : folders) {
if (this.myFolder.getFileGroup().equals(folder.getFileGroup()) && folder != myFolder) {
Helper.setErrorMessage("errorDuplicateFilegroup",
new Object[] {ObjectType.FOLDER.getTranslationPlural() });
}
}
}
}
/**
* Delete folder.
*
* @return page
*/
public String deleteFolder() {
if (Objects.isNull(myFolder.getId())) {
project.getFolders().remove(myFolder);
} else {
deletedFolders.add(this.myFolder.getId());
}
return this.stayOnCurrentPage;
}
/**
* Return list of templates assignable to this project. Templates are
* assignable when they are not assigned already to this project and they
* belong to the same client as the project and user which edits this
* project.
*
* @return list of assignable templates
*/
public List<TemplateDTO> getTemplates() {
try {
return ServiceManager.getTemplateService().findAllAvailableForAssignToProject(this.project.getId());
} catch (DataException e) {
Helper.setErrorMessage(ERROR_LOADING_MANY, new Object[] {ObjectType.TEMPLATE.getTranslationPlural() },
logger, e);
return new LinkedList<>();
}
}
/**
* Get import configurations.
*
* @return import configurations
*/
public List<ImportConfiguration> getImportConfigurations() {
try {
return ServiceManager.getImportConfigurationService().getAll();
} catch (DAOException e) {
Helper.setErrorMessage(e);
return Collections.emptyList();
}
}
/**
* Add template to project.
*
* @return stay on the same page
*/
public String addTemplate() {
int templateId = 0;
String templateIdString = Helper.getRequestParameter(ID_PARAMETER);
if (Objects.nonNull(templateIdString)) {
try {
templateId = Integer.parseInt(templateIdString);
Template template = ServiceManager.getTemplateService().getById(templateId);
if (!this.project.getTemplates().contains(template)) {
this.project.getTemplates().add(template);
template.getProjects().add(this.project);
}
} catch (DAOException e) {
Helper.setErrorMessage(ERROR_DATABASE_READING,
new Object[] {ObjectType.TEMPLATE.getTranslationSingular(), templateId }, logger, e);
} catch (NumberFormatException e) {
Helper.setErrorMessage(e.getLocalizedMessage(), logger, e);
}
} else {
Helper.setErrorMessage(ERROR_PARAMETER_MISSING, new Object[] {ID_PARAMETER});
}
return this.stayOnCurrentPage;
}
/**
* Remove template from project.
*
* @return stay on the same page
*/
public String deleteTemplate() {
String templateIdString = Helper.getRequestParameter(ID_PARAMETER);
if (Objects.nonNull(templateIdString)) {
try {
int templateId = Integer.parseInt(templateIdString);
for (Template template : this.project.getTemplates()) {
if (template.getId().equals(templateId)) {
this.project.getTemplates().remove(template);
template.getProjects().remove(this.project);
this.deletedTemplates.add(template);
break;
}
}
} catch (NumberFormatException e) {
Helper.setErrorMessage(e.getLocalizedMessage(), logger, e);
}
} else {
Helper.setErrorMessage(ERROR_PARAMETER_MISSING, new Object[] {ID_PARAMETER});
}
return this.stayOnCurrentPage;
}
/**
* Switch the lock status of the form.
*/
public void switchLock() {
locked = !locked;
}
/**
* Gets the locked status of the form.
*
* @return te value of locked
*/
public boolean isLocked() {
return locked;
}
/**
* Get project.
*
* @return Project object
*/
public Project getProject() {
return this.project;
}
/**
* Set my project.
*
* @param project
* Project object
*/
public void setProject(Project project) {
// has to be called if a page back move was done
cancel();
this.project = project;
hasProcesses = !project.getProcesses().isEmpty();
}
/**
* Set copy templates.
*
* @param copyTemplates
* as boolean
*/
public void setCopyTemplates(boolean copyTemplates) {
this.copyTemplates = copyTemplates;
}
/**
* Get copy templates.
*
* @return value of copy templates
*/
public boolean isCopyTemplates() {
return copyTemplates;
}
/**
* The need to commit deleted folders only after the save action requires a
* filter, so that those folders marked for delete are not shown anymore.
*
* @return modified ArrayList
*/
public List<Folder> getFolderList() {
List<Folder> filteredFolderList = new ArrayList<>(this.project.getFolders());
for (Integer id : this.deletedFolders) {
for (Folder f : this.project.getFolders()) {
if (Objects.isNull(f.getId()) ? Objects.isNull(id) : f.getId().equals(id)) {
filteredFolderList.remove(f);
break;
}
}
}
return filteredFolderList;
}
/**
* The need to commit deleted folders only after the save action requires a
* filter, so that those folders marked for delete are not shown anymore.
*
* @return modified ArrayList
*/
public List<SelectItem> getSelectableFolders() {
return getFolderList().stream().map(folder -> new SelectItem(folder.getFileGroup(), folder.toString()))
.collect(Collectors.toList());
}
/**
* Checks if folder list contains audio folder.
*
* @return true if folder list contains audio folder
*/
public boolean hasAudioFolder() {
return getFolderList().stream().anyMatch(folder -> folder.getMimeType().startsWith(MIMETYPE_PREFIX_AUDIO));
}
/**
* Checks if folder list contains video folder.
*
* @return true if folder list contains video folder
*/
public boolean hasVideoFolder() {
return getFolderList().stream().anyMatch(folder -> folder.getMimeType().startsWith(MIMETYPE_PREFIX_VIDEO));
}
private Map<String, Folder> getFolderMap() {
return getFolderList().parallelStream().collect(Collectors.toMap(Folder::getFileGroup, Function.identity()));
}
/**
* Returns the folder currently under edit in the pop-up dialog.
*
* @return the folder currently under edit
*/
public Folder getMyFolder() {
return this.myFolder;
}
/**
* Sets the folder currently under edit in the pop-up dialog.
*
* @param myFolder
* folder to set to be under edit now
*/
public void setMyFolder(Folder myFolder) {
this.myFolder = myFolder;
this.generator = new FolderGenerator(myFolder);
}
/**
* Returns an encapsulation to access the generator properties of the folder
* in a JSF-friendly way.
*
* @return the generator controller
*/
public FolderGenerator getGenerator() {
return generator;
}
/**
* Returns the list of possible MIME types to display them in the drop-down
* select.
*
* @return possible MIME types
*/
public Map<String, String> getMimeTypes() {
if (mimeTypes.isEmpty()) {
try {
Locale language = FacesContext.getCurrentInstance().getViewRoot().getLocale();
List<LanguageRange> languages = Collections.singletonList(new LanguageRange(language.toLanguageTag()));
mimeTypes = FileFormatsConfig.getFileFormats().parallelStream()
.collect(Collectors.toMap(locale -> locale.getLabel(languages), FileFormat::getMimeType,
(prior, recent) -> recent, TreeMap::new));
} catch (JAXBException | RuntimeException e) {
Helper.setErrorMessage(ERROR_READING, new Object[] {e.getMessage() }, logger, e);
}
}
return mimeTypes;
}
/**
* Returns the folder to use as source for generation of derived resources
* of this project.
*
* @return the source folder for generation
*/
public String getGeneratorSource() {
Folder source = project.getGeneratorSource();
return Objects.isNull(source) ? null : source.getFileGroup();
}
/**
* Sets the folder to use as source for generation of derived resources of
* this project.
*
* @param generatorSource
* source folder for generation to set
*/
public void setGeneratorSource(String generatorSource) {
project.setGeneratorSource(getFolderMap().get(generatorSource));
}
/**
* Returns the folder to use for the media view.
*
* @return media view folder
*/
public String getMediaView() {
Folder mediaView = project.getMediaView();
return Objects.isNull(mediaView) ? null : mediaView.getFileGroup();
}
/**
* Sets the folder to use for the media view.
*
* @param mediaView
* media view folder
*/
public void setMediaView(String mediaView) {
project.setMediaView(getFolderMap().get(mediaView));
}
/**
* Returns the folder to use for the audio media view.
*
* @return audio media view folder
*/
public String getAudioMediaView() {
Folder audioMediaView = project.getAudioMediaView();
return Objects.isNull(audioMediaView) ? null : audioMediaView.getFileGroup();
}
/**
* Sets the folder to use for the media view.
*
* @param audioMediaView
* audio media view folder
*/
public void setAudioMediaView(String audioMediaView) {
project.setAudioMediaView(getFolderMap().get(audioMediaView));
}
/**
* Returns the folder to use for the video media view.
*
* @return video media view folder
*/
public String getVideoMediaView() {
Folder videoMediaView = project.getVideoMediaView();
return Objects.isNull(videoMediaView) ? null : videoMediaView.getFileGroup();
}
/**
* Sets the folder to use for the media view.
*
* @param videoMediaView
* video media view folder
*/
public void setVideoMediaView(String videoMediaView) {
project.setVideoMediaView(getFolderMap().get(videoMediaView));
}
/**
* Returns the folder to use for preview.
*
* @return preview folder
*/
public String getPreview() {
Folder preview = project.getPreview();
return Objects.isNull(preview) ? null : preview.getFileGroup();
}
/**
* Sets the folder to use for preview.
*
* @param preview
* preview folder
*/
public void setPreview(String preview) {
project.setPreview(getFolderMap().get(preview));
}
/**
* Returns the folder to use for audio preview.
*
* @return audio preview folder
*/
public String getAudioPreview() {
Folder audioPreview = project.getAudioPreview();
return Objects.isNull(audioPreview) ? null : audioPreview.getFileGroup();
}
/**
* Sets the folder to use for audio preview.
*
* @param audioPreview
* audio preview folder
*/
public void setAudioPreview(String audioPreview) {
project.setAudioPreview(getFolderMap().get(audioPreview));
}
/**
* Returns the folder to use for video preview.
*
* @return video preview folder
*/
public String getVideoPreview() {
Folder videoPreview = project.getVideoPreview();
return Objects.isNull(videoPreview) ? null : videoPreview.getFileGroup();
}
/**
* Sets the folder to use for video preview.
*
* @param videoPreview
* video preview folder
*/
public void setVideoPreview(String videoPreview) {
project.setVideoPreview(getFolderMap().get(videoPreview));
}
/**
* Method being used as viewAction for project edit form.
*
* @param id
* ID of the ruleset to load
*/
public void loadProject(int id) {
SecurityAccessController securityAccessController = new SecurityAccessController();
try {
if (!securityAccessController.hasAuthorityToEditProject(id)) {
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
context.redirect(DEFAULT_LINK);
}
} catch (IOException e) {
Helper.setErrorMessage(ERROR_LOADING_ONE, new Object[] {ObjectType.PROJECT.getTranslationSingular(), id },
logger, e);
}
try {
if (!Objects.equals(id, 0)) {
setProject(ServiceManager.getProjectService().getById(id));
this.locked = true;
}
setSaveDisabled(true);
} catch (DAOException e) {
Helper.setErrorMessage(ERROR_LOADING_ONE, new Object[] {ObjectType.PROJECT.getTranslationSingular(), id },
logger, e);
}
}
/**
* Return list of projects.
*
* @return list of projects
*/
public List<ProjectDTO> getProjects() {
try {
return ServiceManager.getProjectService().findAll();
} catch (DataException e) {
Helper.setErrorMessage(ERROR_LOADING_MANY, new Object[] {ObjectType.PROJECT.getTranslationPlural() },
logger, e);
return new LinkedList<>();
}
}
/**
* Set referring view which will be returned when the user clicks "save" or
* "cancel" on the project edit page.
*
* @param referer
* the referring view
*/
public void setProjectEditReferer(String referer) {
if (!referer.isEmpty()) {
if ("projects".equals(referer)) {
this.projectEditReferer = referer;
} else {
this.projectEditReferer = DEFAULT_LINK;
}
}
}
/**
* Get project edit page referring view.
*
* @return project edit page referring view
*/
public String getProjectEditReferer() {
return this.projectEditReferer;
}
/**
* Return whether project has processes or not.
*
* @return whether project has processes or not
*/
public Boolean hasProcesses() {
return hasProcesses;
}
}