/*
* Copyright (C) 2010 - 2014 Converge Consulting
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package dk.i2m.converge.jsf.beans;
import dk.i2m.converge.core.workflow.WorkflowStateTransitionException;
import dk.i2m.commons.BeanComparator;
import dk.i2m.converge.core.ConfigurationKey;
import dk.i2m.converge.core.DataNotFoundException;
import dk.i2m.converge.core.EnrichException;
import dk.i2m.converge.core.content.*;
import dk.i2m.converge.core.content.catalogue.Catalogue;
import dk.i2m.converge.core.content.catalogue.MediaItem;
import dk.i2m.converge.core.content.catalogue.MediaItemRendition;
import dk.i2m.converge.core.content.catalogue.MediaItemStatus;
import dk.i2m.converge.core.content.catalogue.Rendition;
import dk.i2m.converge.core.metadata.*;
import dk.i2m.converge.core.plugin.WorkflowValidatorException;
import dk.i2m.converge.core.search.QueueEntryOperation;
import dk.i2m.converge.core.search.QueueEntryType;
import dk.i2m.converge.core.security.UserAccount;
import dk.i2m.converge.core.security.UserRole;
import dk.i2m.converge.core.utils.StringUtils;
import dk.i2m.converge.core.workflow.*;
import dk.i2m.converge.domain.search.SearchResult;
import dk.i2m.converge.domain.search.SearchResults;
import dk.i2m.converge.ejb.facades.*;
import dk.i2m.converge.ejb.services.ConfigurationServiceLocal;
import dk.i2m.converge.ejb.services.MetaDataServiceLocal;
import dk.i2m.jsf.JsfUtils;
import java.io.IOException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import org.apache.commons.io.FilenameUtils;
import org.richfaces.component.html.HtmlTree;
import org.richfaces.event.NodeSelectedEvent;
import org.richfaces.event.UploadEvent;
import org.richfaces.model.UploadItem;
/**
* Managed backing bean for {@code /NewsItem.jspx}. The backing bean is kept
* alive by the JSF file. Loading a news item is done by setting the ID of the
* item using {@link NewsItem#setId(java.lang.Long)}.
*
* @author Allan Lykke Christensen
*/
public class NewsItem {
private static final Logger LOG = Logger.getLogger(NewsItem.class.getName());
@EJB
private CatalogueFacadeLocal catalogueFacade;
@EJB
private NewsItemFacadeLocal newsItemFacade;
@EJB
private MetaDataFacadeLocal metaDataFacade;
@EJB
private MetaDataServiceLocal metaDataService;
@EJB
private OutletFacadeLocal outletFacade;
@EJB
private UserFacadeLocal userFacade;
@EJB
private SearchEngineLocal searchEngine;
@EJB
private ConfigurationServiceLocal cfgService;
private dk.i2m.converge.core.content.NewsItem selectedNewsItem = null;
private WorkflowStep selectedStep = null;
private WorkflowStateTransition selectedWorkflowStateTransition;
private NewsItemActor selectedActor;
private NewsItemActor newActor = new NewsItemActor();
private Concept selectedMetaDataConcept;
private Long id = 0L;
private DataModel versions = new ListDataModel();
private boolean validWorkflowStep = false;
private String comment = "";
private boolean readOnly = false;
private boolean pullbackAvailable = false;
private ContentItemPermission permission;
private NewsItemMediaAttachment selectedAttachment;
private String keyword = "";
private DataModel searchResults = new ListDataModel();
private Long selectedMediaItemId = null;
private NewsItemMediaAttachment deleteMediaItem = null;
private String newConcept = "";
/**
* Dev Note: Could not use a Concept object for direct entry as it is
* abstract.
*/
private String newConceptName = "";
/**
* Dev Note: Could not use a Concept object for direct entry as it is
* abstract.
*/
private String newConceptDescription = "";
/**
* Dev Note: Could not use a Concept object for direct entry as it is
* abstract.
*/
private String conceptType = "";
private boolean conceptAdded = false;
private Map<String, Boolean> fieldVisibible = new HashMap<String, Boolean>();
private Date editionDate;
private NewsItemPlacement selectedNewsItemPlacement;
private EditionCandidate editionCandidate;
private Map<String, EditionCandidate> editionCandidates = new LinkedHashMap<String, EditionCandidate>();
private Catalogue selectedCatalogue = null;
private boolean showClosedEditions = false;
private Map<String, Concept> suggestedConcepts = new LinkedHashMap<String, Concept>();
private List<Concept> selectedConcepts = new ArrayList<Concept>();
private Long uploadedMediaItem = 0L;
private SearchResults lastSearch = new SearchResults();
/**
* Creates a new instance of {@link NewsItem}.
*/
public NewsItem() {
}
/**
* Event handler for suggesting concepts based on inputted string. Note that
* {@link dk.i2m.converge.core.metadata.Subject}s are not returned as they
* are selected through the subject selection dialog.
*
* @param suggestion String for which to base the suggestions
* @return {@link List} of suggested {@link Concept}s based on
* {@code suggestion}
*/
public List<Concept> onConceptSuggestion(Object suggestion) {
String conceptName = (String) suggestion;
List<Concept> suggestedConcepts = new ArrayList<Concept>();
suggestedConcepts = metaDataFacade.findConceptsByName(conceptName,
Person.class, GeoArea.class, PointOfInterest.class,
Organisation.class);
return suggestedConcepts;
}
public void onAddConcept(ActionEvent event) {
try {
Concept concept = metaDataFacade.findConceptByName(newConcept);
if (!this.selectedNewsItem.getConcepts().contains(concept)) {
this.selectedNewsItem.getConcepts().add(concept);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(),
"NewsItem_CONCEPT_X_ADDED_TO_NEWS_ITEM_Y",
new Object[]{concept.getFullTitle(), selectedNewsItem.
getTitle()});
}
this.newConcept = "";
conceptAdded = true;
onAutoSave(event);
} catch (DataNotFoundException ex) {
this.conceptType = "";
this.newConceptName = this.newConcept;
conceptAdded = false;
}
}
public void onAddNewConcept(ActionEvent event) {
Concept c = null;
if ("ORGANISATION".equalsIgnoreCase(conceptType)) {
c = new Organisation(newConceptName, newConceptDescription);
} else if ("PERSON".equalsIgnoreCase(conceptType)) {
c = new Person(newConceptName, newConceptDescription);
} else if ("LOCATION".equalsIgnoreCase(conceptType)) {
c = new GeoArea(newConceptName, newConceptDescription);
} else if ("POI".equalsIgnoreCase(conceptType)) {
c = new PointOfInterest(newConceptName, newConceptDescription);
} else {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_CONCEPT_TYPE_MISSING");
}
if (c != null) {
c = metaDataFacade.create(c);
if (!this.selectedNewsItem.getConcepts().contains(c)) {
this.selectedNewsItem.getConcepts().add(c);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(),
"NewsItem_CONCEPT_X_ADDED_TO_NEWS_ITEM_Y",
new Object[]{c.getFullTitle(),
selectedNewsItem.getTitle()});
onAutoSave(event);
}
this.newConcept = "";
}
}
public void onSelectSubject(NodeSelectedEvent event) {
HtmlTree tree = (HtmlTree) event.getComponent();
Subject subj = (Subject) tree.getRowData();
this.selectedNewsItem.getConcepts().add(subj);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(),
"NewsItem_CONCEPT_X_ADDED_TO_NEWS_ITEM_Y",
new Object[]{subj.getFullTitle(), selectedNewsItem.getTitle()});
}
/**
* Gets the unique identifier of the loaded news item.
*
* @return Unique identifier of the loaded news item
*/
public Long getId() {
return id;
}
/**
* Get the auto-save interval in milliseconds.
*
* @return Auto-save interval in milliseconds. A value less than 1 indicates
* that auto-save is disabled
*/
public Integer getAutoSaveInterval() {
return cfgService.getInteger(ConfigurationKey.AUTO_SAVE_INTERVAL);
}
/**
* Determines if auto-save is enabled.
*
* @return {@code true} if auto-save is enabled, otherwise {@code false}
*/
public boolean isAutoSaveEnabled() {
if (getAutoSaveInterval() > 0) {
return true;
} else {
return false;
}
}
/**
* Sets the id of the news item to load. Upon setting the identifier, the
* news item will be checked-out from the database.
*
* @param id Unique identifier of the news item to load
*/
public void setId(Long id) {
LOG.log(Level.FINE, "Setting News Item #{0}", id);
this.id = id;
if (id == null) {
return;
}
if (selectedNewsItem == null || (selectedNewsItem.getId() != id)) {
String username = getUser().getUsername();
LOG.log(Level.FINE,
"Checking if {0} is permitted to open news item #{1}",
new Object[]{username, id});
NewsItemHolder nih;
try {
nih = newsItemFacade.checkout(id);
this.permission = nih.getPermission();
this.readOnly = nih.isReadOnly();
this.selectedNewsItem = nih.getNewsItem();
this.pullbackAvailable = nih.isPullbackAvailable();
this.fieldVisibible = nih.getFieldVisibility();
this.versions = new ListDataModel(nih.getVersions());
if (!nih.isCheckedOut() && (this.permission
== ContentItemPermission.USER || this.permission
== ContentItemPermission.ROLE)) {
JsfUtils.createMessage("frmPage",
FacesMessage.SEVERITY_ERROR, Bundle.i18n.name(),
"NewsItem_OPEN_READ_ONLY",
new Object[]{selectedNewsItem.getCheckedOutBy().
getFullName()});
}
} catch (DataNotFoundException ex) {
this.permission = ContentItemPermission.UNAUTHORIZED;
this.readOnly = true;
this.selectedNewsItem = null;
this.pullbackAvailable = false;
}
}
}
/**
* Determines if a news item has been loaded. If a news item cannot be
* retrieved from {@link NewsItem#getSelectedNewsItem()} it is not loaded.
*
* @return {@code true} if a news item has been selected and loaded,
* otherwise {@code false}
*/
public boolean isNewsItemLoaded() {
if (getSelectedNewsItem() == null) {
return false;
} else {
return true;
}
}
public void setSelectedNewsItem(
dk.i2m.converge.core.content.NewsItem newsItem) {
this.selectedNewsItem = newsItem;
}
public dk.i2m.converge.core.content.NewsItem getSelectedNewsItem() {
return selectedNewsItem;
}
/**
* Gets a {@link Map} containing the visibility indicators for each field.
* The Map key is the name of the news item field, corresponding to the full
* name of a {@link NewsItemField}.
*
* @return Visibility indicators for the news item fields
*/
public Map<String, Boolean> getFieldVisible() {
return this.fieldVisibible;
}
public String onSubmit() {
if (getSelectedStep() == null) {
try {
selectedNewsItem = newsItemFacade.checkin(selectedNewsItem);
} catch (LockingException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_COULD_NOT_SAVE_LOCKED");
return null;
}
return "/inbox";
} else {
try {
selectedNewsItem = newsItemFacade.step(selectedNewsItem,
selectedStep.getId(), getComment());
comment = "";
return "/inbox";
} catch (WorkflowStateTransitionException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "Generic_AN_ERROR_OCCURRED_X",
new Object[]{ex.getMessage()});
return null;
}
}
}
public void onApply(ActionEvent event) {
try {
selectedNewsItem = newsItemFacade.save(selectedNewsItem);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(), "NewsItem_CHANGES_SAVED");
} catch (LockingException ex) {
LOG.log(Level.INFO, ex.getMessage());
LOG.log(Level.FINE, "", ex);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_COULD_NOT_SAVE_LOCKED");
}
}
/**
* Event handler invoked upon executing the periodic auto-save poller.
*
* @param event Event that invoked the auto-save
*/
public void onAutoSave(ActionEvent event) {
try {
selectedNewsItem = newsItemFacade.save(selectedNewsItem);
} catch (LockingException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_COULD_NOT_SAVE_LOCKED");
}
}
public String onSave() {
try {
selectedNewsItem = newsItemFacade.checkin(selectedNewsItem);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(), "NewsItem_STORY_SAVED", null);
return "/inbox";
} catch (LockingException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_COULD_NOT_SAVE_LOCKED");
return null;
}
}
public String onClose() {
newsItemFacade.revokeLock(selectedNewsItem.getId());
return "/inbox";
}
public void onPullback(ActionEvent event) {
try {
newsItemFacade.pullback(selectedNewsItem.getId());
Long theId = selectedNewsItem.getId();
selectedNewsItem = null;
setId(theId);
} catch (LockingException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_COULD_NOT_PULLBACK");
} catch (WorkflowStateTransitionException ex) {
LOG.log(Level.SEVERE, "", ex);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_COULD_NOT_PULLBACK_X",
new Object[]{ex.getMessage()});
}
}
public void onActorSelect(ActionEvent event) {
this.newActor = new NewsItemActor();
}
public void setDeleteActor(NewsItemActor actor) {
if (actor != null) {
try {
selectedNewsItem.getActors().remove(actor);
selectedNewsItem = newsItemFacade.save(selectedNewsItem);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(), "NewsItem_ACTOR_REMOVED");
} catch (LockingException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "Generic_AN_ERROR_OCCURRED_X",
new Object[]{ex.getMessage()});
}
}
}
public void setUpdateAttachment(NewsItemMediaAttachment attachment) {
attachment = newsItemFacade.update(attachment);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(), "NewsItem_MEDIA_ATTACHMENT_UPDATED");
}
public void onAddActor(ActionEvent event) {
if (this.newActor != null && this.newActor.getRole() != null
&& this.newActor.getUser() != null) {
boolean dup = false;
for (NewsItemActor nia : selectedNewsItem.getActors()) {
if (nia.getRole().equals(this.newActor.getRole())
&& nia.getUser().equals(this.newActor.getUser())) {
dup = true;
JsfUtils.createMessage("frmPage",
FacesMessage.SEVERITY_ERROR, Bundle.i18n.name(),
"NewsItem_DUPLICATE_USER_ROLE",
new Object[]{this.newActor.getUser().getFullName(),
this.newActor.getRole().getName()});
}
}
if (!dup) {
this.newActor.setNewsItem(selectedNewsItem);
newsItemFacade.addActorToNewsItem(newActor);
try {
selectedNewsItem
= newsItemFacade.findNewsItemById(selectedNewsItem.
getId());
} catch (DataNotFoundException ex) {
JsfUtils.createMessage("frmPage",
FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "Generic_AN_ERROR_OCCURRED_X",
new Object[]{ex.getMessage()});
}
}
this.newActor = new NewsItemActor();
} else {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_SELECT_USER_AND_ROLE");
}
}
public void onSelectMetaData(ActionEvent event) {
try {
selectedNewsItem.getConcepts().add(selectedMetaDataConcept);
selectedNewsItem = newsItemFacade.save(selectedNewsItem);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(),
"NewsItem_META_DATA_X_ADDED_TO_ASSIGNMENT",
new Object[]{getSelectedMetaDataConcept().getName()});
} catch (LockingException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "Generic_AN_ERROR_OCCURRED_X",
new Object[]{ex.getMessage()});
}
}
public void onDeleteSelectedConcept(ActionEvent event) {
try {
selectedNewsItem.getConcepts().remove(selectedMetaDataConcept);
selectedNewsItem = newsItemFacade.save(selectedNewsItem);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(),
"NewsItem_META_DATA_X_REMOVED_FROM_ASSIGNMENT",
new Object[]{getSelectedMetaDataConcept().getName()});
} catch (LockingException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "Generic_AN_ERROR_OCCURRED_X",
new Object[]{ex.getMessage()});
}
}
public void onReplaceHeadline(ActionEvent event) {
selectedNewsItem.setTitle(selectedWorkflowStateTransition.
getHeadlineVersion());
}
public void onReplaceBrief(ActionEvent event) {
selectedNewsItem.setBrief(selectedWorkflowStateTransition.
getBriefVersion());
}
public void onReplaceStory(ActionEvent event) {
selectedNewsItem.setStory(selectedWorkflowStateTransition.
getStoryVersion());
}
/**
* Handler for validating the selected {@link WorkflowStep}.
*
* @param event Event that invoked the handler
*/
public void onValidateWorkflowStep(ActionEvent event) {
this.validWorkflowStep = true;
if (selectedStep == null) {
this.validWorkflowStep = false;
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_VALIDATE_SELECT_OPTION");
}
boolean isRoleValidated = false;
UserRole requiredRole = selectedStep.getToState().getActorRole();
boolean isUserRole = selectedStep.getToState().isUserPermission();
if (isUserRole) {
for (NewsItemActor actor : selectedNewsItem.getActors()) {
if (actor.getRole().equals(requiredRole)) {
// User role was already added
isRoleValidated = true;
break;
}
}
} else {
isRoleValidated = true;
}
if (!isRoleValidated) {
this.validWorkflowStep = false;
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(),
"NewsItem_VALIDATE_MISSING_ROLE", new Object[]{requiredRole.
getName()});
}
for (WorkflowStepValidator validator : selectedStep.getValidators()) {
try {
dk.i2m.converge.core.plugin.WorkflowValidator workflowValidator
= validator.getValidator();
workflowValidator.execute(selectedNewsItem, selectedStep,
validator);
} catch (WorkflowValidatorException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "Generic_AN_ERROR_OCCURRED_X",
new Object[]{ex.getMessage()});
this.validWorkflowStep = false;
}
}
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public NewsItemActor getSelectedActor() {
return selectedActor;
}
public void setSelectedActor(NewsItemActor selectedActor) {
this.selectedActor = selectedActor;
}
public NewsItemActor getNewActor() {
return newActor;
}
public void setNewActor(NewsItemActor newActor) {
this.newActor = newActor;
}
public Map<String, UserRole> getOutletRoles() {
Map<String, UserRole> roles = new LinkedHashMap<String, UserRole>();
for (UserRole role : selectedNewsItem.getOutlet().getRoles()) {
roles.put(role.getName(), role);
}
return roles;
}
public Map<String, UserAccount> getRoleUsers() {
Map<String, UserAccount> users
= new LinkedHashMap<String, UserAccount>();
if (newActor != null && newActor.getRole() != null) {
List<UserAccount> accounts = userFacade.getMembers(
newActor.getRole());
Collections.sort(accounts, new BeanComparator("fullName"));
for (UserAccount user : accounts) {
users.put(user.getFullName(), user);
}
}
return users;
}
public DataModel getMetaDataSubjects() {
return new ListDataModel(metaDataFacade.findConceptByType(Subject.class));
}
public DataModel getMetaDataOrganisations() {
return new ListDataModel(metaDataFacade.findConceptByType(
Organisation.class));
}
public DataModel getMetaDataLocations() {
return new ListDataModel(metaDataFacade.findConceptByType(GeoArea.class));
}
public DataModel getMetaDataPointsOfInterest() {
return new ListDataModel(metaDataFacade.findConceptByType(
PointOfInterest.class));
}
public DataModel getMetaDataPersons() {
return new ListDataModel(metaDataFacade.findConceptByType(Person.class));
}
public Concept getSelectedMetaDataConcept() {
return selectedMetaDataConcept;
}
public void setSelectedMetaDataConcept(Concept selectedMetaDataConcept) {
this.selectedMetaDataConcept = selectedMetaDataConcept;
}
public DataModel getVersions() {
return versions;
}
public void setVersions(DataModel versions) {
this.versions = versions;
}
public WorkflowStateTransition getSelectedWorkflowStateTransition() {
return selectedWorkflowStateTransition;
}
public void setSelectedWorkflowStateTransition(
WorkflowStateTransition selectedWorkflowStateTransition) {
this.selectedWorkflowStateTransition = selectedWorkflowStateTransition;
}
/**
* Gets a {@link Map} of available {@link WorkflowStep}s from the current
* state of the {@link NewsItem}.
*
* @return {@link Map} of available {@link WorkflowStep}s from the current
* state of the {@link NewsItem}
*/
public Map<String, WorkflowStep> getAvailableWorkflowSteps() {
Map<String, WorkflowStep> steps
= new LinkedHashMap<String, WorkflowStep>();
for (WorkflowStep step : getSelectedNewsItem().getCurrentState().
getNextStates()) {
boolean isValidForUser = !Collections.disjoint(step.getValidFor(),
getUser().getUserRoles());
if (step.isValidForAll() || isValidForUser) {
steps.put(step.getName(), step);
}
}
return steps;
}
/**
* Gets the selected {@link WorkflowStep}.
*
* @return {@link WorkflowStep} selected
*/
public WorkflowStep getSelectedStep() {
return selectedStep;
}
/**
* Sets the selected {@link WorkflowStep}. Upon selecting a
* {@link WorkflowStep}, validation will occur.
*
* @param selectedStep {@link WorkflowStep} selected
*/
public void setSelectedStep(WorkflowStep selectedStep) {
this.selectedStep = selectedStep;
}
public NewsItemMediaAttachment getSelectedAttachment() {
return selectedAttachment;
}
public void setSelectedAttachment(NewsItemMediaAttachment selectedAttachment) {
this.selectedAttachment = selectedAttachment;
}
/**
* Determines if the selected {@link WorkflowStep} is valid.
*
* @return {@code true} if the selected {@link WorkflowStep} is valid,
* otherwise {@code false}
*/
public boolean isValidWorkflowStep() {
return validWorkflowStep;
}
/**
* Determines if the current user is the current actor of the
* {@link NewsItem}.
*
* @return {@code true} if the current user is among the current actors of
* the {@link NewsItem}, otherwise {@code false}
*/
public boolean isCurrentActor() {
boolean currentActor = false;
if (permission == null) {
return false;
}
switch (permission) {
case USER:
case ROLE:
currentActor = true;
break;
default:
currentActor = false;
}
return currentActor;
}
public boolean isAuthorized() {
if (permission == null) {
return false;
}
return !(permission == ContentItemPermission.UNAUTHORIZED);
}
public boolean isPullbackAvailable() {
return this.pullbackAvailable;
}
public boolean isEditable() {
return isCurrentActor() && !isReadOnly();
}
/**
* Determines if the news item is locked and thereby read-only or if the
* user is a part of the actors but not the current actor.
*
* @return {@code true} if the news item is locked, otherwise {@code false}
*/
public boolean isReadOnly() {
return readOnly || (ContentItemPermission.ACTOR == permission);
}
public Map<String, Edition> getOpenEditions() {
Map<String, Edition> editions = new LinkedHashMap<String, Edition>();
if (selectedNewsItem.getOutlet() != null) {
for (Edition edition : outletFacade.findEditionsByStatus(true,
selectedNewsItem.getOutlet())) {
editions.put(edition.getFriendlyName(), edition);
}
}
return editions;
}
/**
* Gets a {@link Map} of active sections for the {@link Outlet} in the
* selected {@link NewsItemPlacement}.
*
* @return {@link Map} of active sections
*/
public Map<String, Section> getSections() {
Map<String, Section> sections = new LinkedHashMap<String, Section>();
for (Section section : selectedNewsItemPlacement.getOutlet().
getActiveSections()) {
sections.put(section.getFullName(), section);
}
return sections;
}
private UserAccount getUser() {
return (UserAccount) JsfUtils.getValueOfValueExpression(
"#{userSession.user}");
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public DataModel getSearchResults() {
return searchResults;
}
public void setSearchResults(DataModel searchResults) {
this.searchResults = searchResults;
}
public SearchResults getLastSearch() {
return lastSearch;
}
/**
* Event handler for starting the search.
*
* @param event Event that invoked the handler
*/
public void onSearch(ActionEvent event) {
if (!getKeyword().trim().isEmpty()) {
lastSearch = searchEngine.search(getKeyword(), 0, 50, "type:Media");
List<SearchResult> results = lastSearch.getHits();
List<SearchResult> realResults = new ArrayList<SearchResult>();
for (SearchResult result : results) {
realResults.add(result);
}
if (realResults.isEmpty()) {
searchResults = new ListDataModel(new ArrayList());
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(),
"NewsItem_NO_MEDIA_ITEM_MATCHING_X",
new Object[]{getKeyword()});
} else {
for (SearchResult hit : realResults) {
hit.setLink(
MessageFormat.format(hit.getLink(),
new Object[]{
JsfUtils.getValueOfValueExpression(
"#{facesContext.externalContext.request.contextPath}")}));
}
searchResults = new ListDataModel(realResults);
}
}
}
public void setUploadedMediaItem(String id) {
id = id.replaceAll("SUCCESS" + System.getProperty("line.separator"), "");
this.uploadedMediaItem = Long.valueOf(id.trim());
setSelectedMediaItemId(this.uploadedMediaItem);
}
public Long getSelectedMediaItemId() {
return selectedMediaItemId;
}
public void setSelectedMediaItemId(Long selectedMediaItemId) {
this.selectedMediaItemId = selectedMediaItemId;
if (this.selectedMediaItemId != null) {
this.selectedAttachment = new NewsItemMediaAttachment();
this.selectedAttachment.setNewsItem(selectedNewsItem);
try {
MediaItem mi = catalogueFacade.findMediaItemById(
this.selectedMediaItemId);
this.selectedAttachment.setMediaItem(mi);
this.selectedAttachment.setCaption(this.selectedAttachment.
getMediaItem().getDescription());
} catch (DataNotFoundException ex) {
searchEngine.addToIndexQueue(QueueEntryType.MEDIA_ITEM,
this.selectedMediaItemId, QueueEntryOperation.REMOVE);
}
}
}
/**
* Event handler for attaching a {@link MediaItem} to the
* {@link dk.i2m.converge.core.content.NewsItem}.
*
* @param event Event that invoked the handler
*/
public void onUseAttachment(ActionEvent event) {
boolean isNewUpload = this.uploadedMediaItem.longValue()
== this.selectedAttachment.getMediaItem().getId().longValue();
if (isNewUpload) {
try {
MediaItem mediaItem = catalogueFacade.findMediaItemById(
this.uploadedMediaItem);
mediaItem.setDescription(selectedAttachment.getCaption());
mediaItem.setByLine(
selectedAttachment.getMediaItem().getByLine());
mediaItem = catalogueFacade.update(mediaItem);
selectedAttachment.setMediaItem(mediaItem);
} catch (DataNotFoundException ex) {
LOG.warning(ex.getMessage());
}
}
this.selectedAttachment.setDisplayOrder(this.selectedNewsItem.
getNextAssetAttachmentDisplayOrder());
this.selectedAttachment = newsItemFacade.create(selectedAttachment);
this.selectedNewsItem.getMediaAttachments().add(selectedAttachment);
onPreAttachMediaFile(event);
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_INFO,
Bundle.i18n.name(),
"NewsItem_MEDIA_ITEM_ATTACHED_TO_NEWS_ITEM");
}
public void onPreAttachMediaFile(ActionEvent event) {
this.selectedCatalogue = null;
this.selectedCatalogue = getUser().getDefaultMediaRepository();
this.selectedAttachment = new NewsItemMediaAttachment();
this.selectedAttachment.setNewsItem(selectedNewsItem);
searchResults = new ListDataModel(new ArrayList());
setKeyword("");
}
public class DiscoveredProperty {
private String property = "";
private String value = "";
public DiscoveredProperty() {
}
public DiscoveredProperty(String property, String value) {
this.property = property;
this.value = value;
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public NewsItemMediaAttachment getDeleteMediaItem() {
return deleteMediaItem;
}
public void setDeleteMediaItem(NewsItemMediaAttachment deleteMediaItem) {
this.deleteMediaItem = deleteMediaItem;
if (this.deleteMediaItem != null) {
newsItemFacade.deleteMediaAttachmentById(
this.deleteMediaItem.getId());
this.selectedNewsItem.getMediaAttachments().remove(
this.deleteMediaItem);
}
}
public String getConceptType() {
return conceptType;
}
public void setConceptType(String conceptType) {
this.conceptType = conceptType;
}
public String getNewConcept() {
return newConcept;
}
public void setNewConcept(String newConcept) {
this.newConcept = newConcept;
}
public String getNewConceptDescription() {
return newConceptDescription;
}
public void setNewConceptDescription(String newConceptDescription) {
this.newConceptDescription = newConceptDescription;
}
public String getNewConceptName() {
return newConceptName;
}
public void setNewConceptName(String newConceptName) {
this.newConceptName = newConceptName;
}
public boolean isConceptAdded() {
return conceptAdded;
}
public void setRemoveConcept(Concept concept) {
this.selectedNewsItem.getConcepts().remove(concept);
}
public Date getEditionDate() {
return editionDate;
}
public void setEditionDate(Date editionDate) {
this.editionDate = editionDate;
this.editionCandidates = new LinkedHashMap<String, EditionCandidate>();
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm zzz");
if (editionDate != null) {
java.util.Calendar editionCal = java.util.Calendar.getInstance();
editionCal.setTime(editionDate);
List<EditionCandidate> editions = outletFacade.
findEditionCandidatesByDate(getSelectedNewsItemPlacement().
getOutlet(), editionCal, showClosedEditions);
Collections.sort(editions, new BeanComparator("publicationDate"));
for (EditionCandidate e : editions) {
String label = "";
if (e.getPublicationDate() != null) {
label = formatter.format(e.getPublicationDate().getTime());
}
this.editionCandidates.put(label, e);
}
}
}
public void onToggleShowClosedEditions(ActionEvent event) {
setEditionDate(editionDate);
}
public boolean isShowClosedEditions() {
return showClosedEditions;
}
public void setShowClosedEditions(boolean showClosedEditions) {
this.showClosedEditions = showClosedEditions;
}
public void onChangePlacementOutlet(ValueChangeEvent event) {
this.selectedNewsItemPlacement.setOutlet((Outlet) event.getNewValue());
setEditionDate(getEditionDate());
}
/**
* Gets a {@link Map} of {@link Edition} candidates based on the selected
* edition date.
*
* @return {@link Map} of {@link Edition} candidates based on the selected
* edition date
*/
public Map<String, EditionCandidate> getEditionCandidates() {
return this.editionCandidates;
}
public void onNewPlacement(ActionEvent event) {
this.selectedNewsItemPlacement = new NewsItemPlacement();
this.selectedNewsItemPlacement.setNewsItem(selectedNewsItem);
if (getUser().getDefaultOutlet() != null) {
this.selectedNewsItemPlacement.setOutlet(
getUser().getDefaultOutlet());
} else {
this.selectedNewsItemPlacement.setOutlet(
selectedNewsItem.getOutlet());
}
if (getUser().getDefaultSection() != null
&& this.selectedNewsItemPlacement.getOutlet() != null
&& getUser().getDefaultSection().getOutlet().equals(this.selectedNewsItemPlacement.
getOutlet())) {
this.selectedNewsItemPlacement.setSection(getUser().
getDefaultSection());
}
this.editionCandidates = new LinkedHashMap<String, EditionCandidate>();
this.editionCandidate = null;
this.editionDate = null;
}
public void onAddPlacement(ActionEvent event) {
if (getEditionCandidate() == null) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(),
"NewsItem_OUTLET_PLACEMENT_SELECT_EDITION");
return;
}
if (getEditionCandidate().isExist()) {
try {
selectedNewsItemPlacement.setEdition(outletFacade.
findEditionById(getEditionCandidate().getEditionId()));
} catch (DataNotFoundException ex) {
LOG.log(Level.INFO,
"Edition {0} could not be found in the database",
getEditionCandidate().getEditionId());
}
} else {
selectedNewsItemPlacement.setEdition(outletFacade.createEdition(
getEditionCandidate()));
}
selectedNewsItemPlacement = newsItemFacade.createPlacement(
selectedNewsItemPlacement);
if (!selectedNewsItem.getPlacements().contains(selectedNewsItemPlacement)) {
selectedNewsItem.getPlacements().add(selectedNewsItemPlacement);
}
}
public void onUpdatePlacement(ActionEvent event) {
if (getEditionCandidate() == null) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(),
"NewsItem_OUTLET_PLACEMENT_SELECT_EDITION");
return;
}
if (getEditionCandidate().isExist()) {
try {
selectedNewsItemPlacement.setEdition(outletFacade.
findEditionById(getEditionCandidate().getEditionId()));
} catch (DataNotFoundException ex) {
LOG.log(Level.INFO, "Edition {0} does not exist",
getEditionCandidate().getEditionId());
}
} else {
selectedNewsItemPlacement.setEdition(outletFacade.createEdition(
getEditionCandidate()));
}
selectedNewsItemPlacement = newsItemFacade.updatePlacement(
selectedNewsItemPlacement);
if (!selectedNewsItem.getPlacements().contains(selectedNewsItemPlacement)) {
selectedNewsItem.getPlacements().add(selectedNewsItemPlacement);
}
}
public void onRemovePlacement(ActionEvent event) {
if (selectedNewsItem.getPlacements().contains(selectedNewsItemPlacement)) {
selectedNewsItem.getPlacements().remove(selectedNewsItemPlacement);
}
newsItemFacade.deletePlacement(selectedNewsItemPlacement);
}
/**
* Event handler for uploading a new {@link MediaItem}.
*
* @param event Event that invoked the handler
* @throws IOException If the file upload could not complete
*/
public void onUploadMediaItem(UploadEvent event) throws IOException {
UploadItem item = event.getUploadItem();
try {
// Find the original rendition (assume this is what is being uploaded)
Rendition rendition = getSelectedCatalogue().getOriginalRendition();
// Create MediaItem placeholder
MediaItem mediaItem = new MediaItem();
mediaItem.setTitle(FilenameUtils.getBaseName(item.getFileName()));
mediaItem.setStatus(MediaItemStatus.SELF_UPLOAD);
mediaItem.setOwner(getUser());
mediaItem.setByLine(getUser().getFullName());
mediaItem.setStatus(MediaItemStatus.SELF_UPLOAD);
mediaItem.setCatalogue(getSelectedCatalogue());
mediaItem = catalogueFacade.create(mediaItem);
// Store MediaItemRendition in Database
MediaItemRendition mediaItemRendition = catalogueFacade.create(
item.getFile(),
mediaItem,
rendition,
item.getFileName(),
item.getContentType(),
true);
LOG.log(Level.FINE, "New media item rendition created: {0}",
mediaItemRendition.getId());
this.uploadedMediaItem = mediaItem.getId();
setSelectedMediaItemId(this.uploadedMediaItem);
} catch (IOException ex) {
JsfUtils.createMessage("frmPage",
FacesMessage.SEVERITY_FATAL,
Bundle.i18n.name(),
"Generic_AN_ERROR_OCCURRED_X",
new Object[]{ex.getMessage()});
LOG.log(Level.SEVERE, "Could not create media item rendition. {0}",
ex.getMessage());
}
}
public NewsItemPlacement getSelectedNewsItemPlacement() {
return selectedNewsItemPlacement;
}
public void setSelectedNewsItemPlacement(
NewsItemPlacement selectedNewsItemPlacement) {
this.selectedNewsItemPlacement = selectedNewsItemPlacement;
if (this.selectedNewsItemPlacement != null
&& this.selectedNewsItemPlacement.getEdition() != null) {
this.editionCandidate
= new EditionCandidate(this.selectedNewsItemPlacement.
getEdition());
}
}
public EditionCandidate getEditionCandidate() {
return editionCandidate;
}
public void setEditionCandidate(EditionCandidate editionCandidate) {
this.editionCandidate = editionCandidate;
}
public Catalogue getSelectedCatalogue() {
return selectedCatalogue;
}
public void setSelectedCatalogue(Catalogue selectedCatalogue) {
this.selectedCatalogue = selectedCatalogue;
}
/**
* Gets the number of columns to display in the grid of attached media
* items.
*
* @return Number of columns to display in the media attachment grid
*/
public int getNumberOfMediaAttachmentsColumns() {
if (selectedNewsItem == null) {
return 0;
} else if (selectedNewsItem.getMediaAttachments().size() < 3) {
return selectedNewsItem.getMediaAttachments().size();
} else {
return 3;
}
}
public void onSuggestConcepts(ActionEvent event) {
if (getSelectedNewsItem().getStory().trim().isEmpty()) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_CONCEPTS_SUGGEST_FAILED_NO_STORY");
return;
}
try {
List<Concept> concepts = metaDataService.enrich(StringUtils.
stripHtml(getSelectedNewsItem().getStory()));
suggestedConcepts = new LinkedHashMap<String, Concept>();
this.selectedConcepts = new ArrayList<Concept>();
ResourceBundle bundle = JsfUtils.getResourceBundle(Bundle.i18n.name());
for (Concept concept : concepts) {
String type = bundle.getString("Generic_" + concept.getType()
+ "_NAME");
suggestedConcepts.put(concept.getName() + " (" + type + ")",
concept);
}
if (suggestedConcepts.isEmpty()) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
Bundle.i18n.name(), "NewsItem_CONCEPTS_SUGGEST_NO_RESULTS");
}
} catch (EnrichException ex) {
JsfUtils.createMessage("frmPage", FacesMessage.SEVERITY_ERROR,
"i18n", "NewsItem_CONCEPTS_SUGGEST_FAILED", null);
}
}
public void onSaveConceptSuggestions(ActionEvent event) {
getSelectedNewsItem().getConcepts().addAll(getSelectedConcepts());
List<Concept> allConcepts = getSelectedNewsItem().getConcepts();
Set<Concept> uniqueConcepts = new HashSet<Concept>(allConcepts);
allConcepts = new ArrayList<Concept>(uniqueConcepts);
getSelectedNewsItem().setConcepts(allConcepts);
onAutoSave(event);
}
public Map<String, Concept> getSuggestedConcepts() {
return suggestedConcepts;
}
public List<Concept> getSelectedConcepts() {
return selectedConcepts;
}
public void setSelectedConcepts(List<Concept> selectedConcepts) {
this.selectedConcepts = selectedConcepts;
}
}