/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.action;
import static org.jboss.seam.international.StatusMessage.Severity.INFO;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Events;
import org.jboss.seam.international.Messages;
import org.jboss.seam.international.StatusMessages;
import org.jboss.seam.wiki.core.action.prefs.CommentsPreferences;
import org.jboss.seam.wiki.core.action.prefs.DocumentEditorPreferences;
import org.jboss.seam.wiki.core.action.prefs.WikiPreferences;
import org.jboss.seam.wiki.core.exception.InvalidWikiRequestException;
import org.jboss.seam.wiki.core.feeds.FeedDAO;
import org.jboss.seam.wiki.core.feeds.FeedEntryManager;
import org.jboss.seam.wiki.core.model.FeedEntry;
import org.jboss.seam.wiki.core.model.WikiDirectory;
import org.jboss.seam.wiki.core.model.WikiDocument;
import org.jboss.seam.wiki.core.model.WikiDocumentDefaults;
import org.jboss.seam.wiki.core.model.WikiFile;
import org.jboss.seam.wiki.core.model.WikiTextMacro;
import org.jboss.seam.wiki.core.template.TemplateRegistry;
import org.jboss.seam.wiki.core.template.WikiDocumentEditorDefaults;
import org.jboss.seam.wiki.core.template.WikiDocumentTemplate;
import org.jboss.seam.wiki.core.wikitext.editor.WikiTextEditor;
import org.jboss.seam.wiki.core.wikitext.renderer.MacroWikiTextRenderer;
import org.jboss.seam.wiki.preferences.Preferences;
import org.jboss.seam.wiki.util.WikiUtil;
@Name("documentHome")
@Scope(ScopeType.CONVERSATION)
public class DocumentHome extends NodeHome<WikiDocument, WikiDirectory> {
/* -------------------------- Context Wiring ------------------------------ */
@In(required = false)
private DocumentHistory documentHistory;
@In
private FeedDAO feedDAO;
@In
private TagEditor tagEditor;
@In("#{preferences.get('DocEditor')}")
private DocumentEditorPreferences docEditorPreferences;
/* -------------------------- Internal State ------------------------------ */
protected WikiTextEditor textEditor = new WikiTextEditor("content");
protected WikiDocument historicalCopy;
protected Boolean minorRevision;
protected boolean pushOnFeeds = false;
protected boolean pushOnSiteFeed = false;
protected boolean isOnSiteFeed = false;
protected List<WikiFile> historicalFiles;
protected Long numOfHistoricalFiles = 0l;
protected String templateType;
/* -------------------------- Basic Overrides ------------------------------ */
@Override
public Class<WikiDocument> getEntityClass() {
return WikiDocument.class;
}
@Override
public WikiDocument findInstance() {
return getWikiNodeDAO().findWikiDocument((Long)getId());
}
@Override
protected WikiDirectory findParentNode(Long parentNodeId) {
return getEntityManager().find(WikiDirectory.class, parentNodeId);
}
@Override
public WikiDocument afterNodeCreated(WikiDocument doc) {
doc = super.afterNodeCreated(doc);
tagEditor.setTags(doc.getTags());
outjectDocumentAndDirectory(doc, getParentNode());
if (templateType != null && !templateType.equals(WikiDocumentDefaults.class.getName())) {
getLog().debug("using custom template class for WikiDocument defaults: " + templateType);
WikiDocumentDefaults defaults;
try {
Class<?> tplClass = Class.forName(templateType);
if (!TemplateRegistry.instance().getTemplateTypes().contains(tplClass)) {
throw new InvalidWikiRequestException("Invalid templateType: " + templateType);
}
if (tplClass.getAnnotation(WikiDocumentTemplate.class).requiresTemplateInstance()) {
getLog().debug("instantiating template " + tplClass.getName() + " with current document instance");
defaults = (WikiDocumentDefaults)tplClass.getConstructor(WikiDocument.class).newInstance(doc);
} else {
getLog().debug("instantiating template " + tplClass.getName() + " with no-arg constructor");
defaults = (WikiDocumentDefaults)tplClass.newInstance();
}
if (WikiDocumentEditorDefaults.class.isAssignableFrom(tplClass)) {
getLog().debug("letting template set editor defaults");
((WikiDocumentEditorDefaults)defaults).setEditorDefaults(this);
}
} catch (Exception ex) {
throw new InvalidWikiRequestException("Invalid templateType: " + templateType);
}
doc.setDefaults(defaults);
}
return doc;
}
@Override
public WikiDocument beforeNodeEditNew(WikiDocument doc) {
doc = super.beforeNodeEditNew(doc);
tagEditor.setTags(doc.getTags());
doc.setEnableComments( Preferences.instance().get(CommentsPreferences.class).getEnableByDefault() );
syncInstanceToEditor(getParentNode().getAreaNumber(), doc);
return doc;
}
@Override
public WikiDocument afterNodeFound(WikiDocument doc) {
doc = super.afterNodeFound(doc);
tagEditor.setTags(doc.getTags());
findHistoricalFiles(doc);
syncMacros(doc);
outjectDocumentAndDirectory(doc, getParentNode());
return doc;
}
@Override
public WikiDocument beforeNodeEditFound(WikiDocument doc) {
doc = super.beforeNodeEditFound(doc);
tagEditor.setTags(doc.getTags());
// Rollback to historical revision?
if (documentHistory != null && documentHistory.getSelectedHistoricalFile() != null) {
getLog().debug("rolling back to revision: " + documentHistory.getSelectedHistoricalFile().getRevision());
// TODO: Avoid cast, make history polymorphic
WikiDocument oldRevision = (WikiDocument)documentHistory.getSelectedHistoricalFile();
doc.rollback(oldRevision);
doc.setWikiname(WikiUtil.convertToWikiName(doc.getName()));
}
isOnSiteFeed = feedDAO.isOnSiteFeed(doc);
syncInstanceToEditor(getParentNode().getAreaNumber(), doc);
copyHistoricalRevision(doc);
return doc;
}
@Override
protected String getEditorWorkspaceDescription(boolean create) {
if (create) {
return Messages.instance().get("lacewiki.label.docEdit.CreateDocument");
} else {
return Messages.instance().get("lacewiki.label.docEdit.EditDocument") + ":" + getInstance().getName();
}
}
/* -------------------------- Custom CUD ------------------------------ */
@Override
protected boolean beforePersist() {
syncEditorToInstance(getParentNode().getAreaNumber(), getInstance());
syncMacros(getInstance());
copyHistoricalRevision(getInstance());
purgeFeedEntries();
// Plain text can only be set on persist(), never changed later with update()
if (textEditor.isValuePlaintext())
getInstance().addHeaderMacro(new WikiTextMacro(WikiDocument.MACRO_DISABLE_CONTENT_MARKUP));
return true;
}
@Override
public String persist() {
String outcome = super.persist();
// Create feed entries (needs identifiers assigned, so we run after persist())
if (outcome != null && isPushOnFeeds()) {
getLog().debug("creating feed entries on parent dirs - and on site feed: " + isPushOnSiteFeed());
if (isPushOnSiteFeed()) isOnSiteFeed = true;
FeedEntry feedEntry =
((FeedEntryManager)Component.getInstance(getFeedEntryManagerName())).createFeedEntry(getInstance());
feedDAO.createFeedEntry(getParentNode(), getInstance(), feedEntry, isPushOnSiteFeed());
getEntityManager().flush();
setPushOnFeeds(false);
setPushOnSiteFeed(false);
}
return outcome;
}
@Override
protected boolean beforeUpdate() {
syncEditorToInstance(getParentNode().getAreaNumber(), getInstance());
syncMacros(getInstance());
// Update feed entries
if (isPushOnFeeds()) {
if (isPushOnSiteFeed()) isOnSiteFeed = true;
FeedEntry feedEntry = feedDAO.findFeedEntry(getInstance());
if (feedEntry == null) {
getLog().debug("creating feed entries on parent dirs - and on site feed: " + isPushOnSiteFeed());
feedEntry = ((FeedEntryManager)Component.getInstance(getFeedEntryManagerName())).createFeedEntry(getInstance());
feedDAO.createFeedEntry(getParentNode(), getInstance(), feedEntry, isPushOnSiteFeed());
} else {
getLog().debug("updating feed entries on parent dirs - and on site feed: " + isPushOnSiteFeed());
((FeedEntryManager)Component.getInstance(getFeedEntryManagerName())).updateFeedEntry(feedEntry, getInstance());
feedDAO.updateFeedEntry(getParentNode(), getInstance(), feedEntry, isPushOnSiteFeed());
}
setPushOnFeeds(false);
setPushOnSiteFeed(false);
}
purgeFeedEntries();
// Write history log and prepare a new copy for further modification
if (!isMinorRevision()) {
if (historicalCopy == null)
throw new IllegalStateException("Call getFormContent() once to create a historical revision");
getLog().debug("storing the historical copy as a new revision");
historicalCopy.setId(getInstance().getId());
historicalCopy.setLastModifiedBy(getCurrentUser());
getWikiNodeDAO().persistHistoricalFile(historicalCopy);
getInstance().incrementRevision();
copyHistoricalRevision(getInstance());
// Reset form
setMinorRevision( docEditorPreferences.getMinorRevisionEnabled() );
}
return true;
}
@Override
public String remove() {
return trash();
}
public String reallyRemove()
{
checkRemovePermissions();
getLog().debug("removing node : " + getInstance());
getNodeRemover().removeDependencies(getInstance());
getEntityManager().remove(getInstance());
getEntityManager().flush();
Events.instance().raiseEvent("Node.removed", getInstance());
return "removed";
}
@Override
protected NodeRemover getNodeRemover() {
return (DocumentNodeRemover)Component.getInstance(DocumentNodeRemover.class);
}
@Override
protected Validatable[] getUpdateValidations() {
return new Validatable[] { textEditor };
}
@Override
protected Validatable[] getPersistValidations() {
return new Validatable[] { textEditor };
}
/* -------------------------- Messages ------------------------------ */
@Override
protected void createdMessage() {
StatusMessages.instance().addFromResourceBundleOrDefault(
INFO,
"lacewiki.msg.Document.Persist",
"Document '{0}' has been saved.",
getInstance().getName()
);
}
@Override
protected void updatedMessage() {
StatusMessages.instance().addFromResourceBundleOrDefault(
INFO,
"lacewiki.msg.Document.Update",
"Document '{0}' has been updated.",
getInstance().getName()
);
}
@Override
protected void deletedMessage() {
StatusMessages.instance().addFromResourceBundleOrDefault(
INFO,
"lacewiki.msg.Document.Delete",
"Document '{0}' has been deleted.",
getInstance().getName()
);
}
/* -------------------------- Internal Methods ------------------------------ */
protected void findHistoricalFiles(WikiDocument doc) {
getLog().debug("Finding number of historical files for: " + doc);
numOfHistoricalFiles= getWikiNodeDAO().findNumberOfHistoricalFiles(doc);
if (isHistoricalFilesPresent()) {
historicalFiles = getWikiNodeDAO().findHistoricalFiles(doc);
}
}
// Wiki text parser and plugins need this
protected void outjectDocumentAndDirectory(WikiDocument doc, WikiDirectory dir) {
if (isPageRootController()) {
if (doc != null) {
getLog().debug("setting current document: " + doc);
Contexts.getConversationContext().set("currentDocument", doc);
}
if (dir != null) {
getLog().debug("setting current directory: " + dir);
Contexts.getConversationContext().set("currentDirectory", dir);
}
}
}
public void syncEditorToInstance(Long areaNumber, WikiDocument doc) {
doc.setContent(textEditor.getValueAndConvertLinks(areaNumber));
if (textEditor.getLinkTargets() != null)
doc.setOutgoingLinks(textEditor.getLinkTargets());
}
public void syncInstanceToEditor(Long areaNumber, WikiDocument doc) {
textEditor.setValueAndConvertLinks(areaNumber, doc.getContent());
textEditor.setRows(docEditorPreferences.getRegularEditAreaRows().intValue());
}
protected void copyHistoricalRevision(WikiDocument doc) {
historicalCopy = new WikiDocument();
historicalCopy.flatCopy(doc, true);
}
protected String getFeedEntryManagerName() {
return "wikiDocumentFeedEntryManager";
}
protected void purgeFeedEntries() {
// Feeds should not be removed by a maintenance thread: If there
// is no activity on the site, feeds shouldn't be empty but show the last updates.
Calendar oldestDate = GregorianCalendar.getInstance();
oldestDate.add(Calendar.DAY_OF_YEAR, -Preferences.instance().get(WikiPreferences.class).getPurgeFeedEntriesAfterDays().intValue());
feedDAO.purgeOldFeedEntries(oldestDate.getTime());
}
/* -------------------------- Public Features ------------------------------ */
public void syncMacros(WikiDocument doc) {
if (doc.getHeader() != null) {
MacroWikiTextRenderer renderer = MacroWikiTextRenderer.renderMacros(doc.getHeader());
doc.setHeaderMacros(renderer.getMacros());
}
if (doc.getContent() != null) {
MacroWikiTextRenderer renderer = MacroWikiTextRenderer.renderMacros(doc.getContent());
doc.setContentMacros(renderer.getMacros());
}
if (doc.getFooter() != null) {
MacroWikiTextRenderer renderer = MacroWikiTextRenderer.renderMacros(doc.getFooter());
doc.setFooterMacros(renderer.getMacros());
}
}
public boolean isMinorRevision() {
// Lazily initalize preferences
if (minorRevision == null)
minorRevision = docEditorPreferences.getMinorRevisionEnabled();
return minorRevision;
}
public void setMinorRevision(boolean minorRevision) { this.minorRevision = minorRevision; }
public boolean isOnSiteFeed() {
return isOnSiteFeed;
}
public boolean isPushOnFeeds() {
return pushOnFeeds;
}
public void setPushOnFeeds(boolean pushOnFeeds) {
this.pushOnFeeds = pushOnFeeds;
}
public boolean isPushOnSiteFeed() {
return pushOnSiteFeed;
}
public void setPushOnSiteFeed(boolean pushOnSiteFeed) {
this.pushOnSiteFeed = pushOnSiteFeed;
}
public boolean isHistoricalFilesPresent() {
return numOfHistoricalFiles != null && numOfHistoricalFiles> 0;
}
public List<WikiFile> getHistoricalFiles() {
return historicalFiles;
}
public TagEditor getTagEditor() {
return tagEditor;
}
public String getTemplateType() {
return templateType;
}
public void setTemplateType(String templateType) {
this.templateType = templateType;
}
public WikiTextEditor getTextEditor() {
return textEditor;
}
}