/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.component.wiki.internal;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.xwiki.bridge.event.ApplicationReadyEvent;
import org.xwiki.bridge.event.WikiReadyEvent;
import org.xwiki.component.annotation.Component;
import org.xwiki.context.Execution;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.observation.EventListener;
import org.xwiki.observation.event.Event;
import org.xwiki.rendering.syntax.Syntax;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.classes.BaseClass;
import com.xpn.xwiki.objects.classes.TextAreaClass;
/**
* Initializes the XClasses required by {@link DefaultWikiComponentBuilder}.
*
* @version $Id: f892b7c957b2d8747c1f6fd6b0675554d648bb4f $
* @since 4.2M3
*/
@Component
@Named("defaultWikiComponentBuilderEventListener")
@Singleton
public class DefaultWikiComponentBuilderEventListener implements EventListener, WikiComponentConstants
{
/**
* The logger to log.
*/
@Inject
private Logger logger;
/**
* Our execution. Needed to access the XWiki context.
*/
@Inject
private Execution execution;
/**
* Used to serialize wiki pages reference in the log.
*/
@Inject
@Named("compactwiki")
private EntityReferenceSerializer<String> compactWikiSerializer;
@Override
public List<Event> getEvents()
{
return Arrays.<Event> asList(new ApplicationReadyEvent(), new WikiReadyEvent());
}
@Override
public String getName()
{
return "defaultWikiComponentBuilderEventListener";
}
@Override
public void onEvent(Event arg0, Object arg1, Object arg2)
{
try {
this.installOrUpdateComponentXClass();
this.installOrUpdateComponentRequirementXClass();
this.installOrUpdateComponentMethodXClass();
this.installOrUpdateComponentInterfaceXClass();
} catch (XWikiException e) {
this.logger.error("Failed to install or update wiki component XClasses", e);
}
}
/**
* Verify that the {@link #INTERFACE_CLASS} exists and is up-to-date (act if not).
*
* @throws com.xpn.xwiki.XWikiException on failure
*/
private void installOrUpdateComponentInterfaceXClass() throws XWikiException
{
XWikiContext xcontext = getXWikiContext();
XWikiDocument doc = xcontext.getWiki().getDocument(INTERFACE_CLASS_REFERENCE, xcontext);
BaseClass bclass = doc.getXClass();
bclass.setDocumentReference(doc.getDocumentReference());
boolean needsUpdate = false;
needsUpdate |= this.initializeXClassDocumentMetadata(doc, "Wiki Component Implements Interface XWiki Class");
needsUpdate |= bclass.addTextField(INTERFACE_NAME_FIELD, "Interface Qualified Name", 30);
if (needsUpdate) {
this.update(doc);
}
}
/**
* Verify that the {@link #COMPONENT_CLASS} exists and is up-to-date (act if not).
*
* @throws XWikiException on failure
*/
private void installOrUpdateComponentXClass() throws XWikiException
{
XWikiContext xcontext = getXWikiContext();
XWikiDocument doc = xcontext.getWiki().getDocument(COMPONENT_CLASS_REFERENCE, xcontext);
BaseClass bclass = doc.getXClass();
bclass.setDocumentReference(doc.getDocumentReference());
boolean needsUpdate = false;
needsUpdate |= initializeXClassDocumentMetadata(doc, "Wiki Component XWiki Class");
needsUpdate |= bclass.addTextField(COMPONENT_ROLE_TYPE_FIELD, "Component Role Type", 30);
needsUpdate |= bclass.addTextField(COMPONENT_ROLE_HINT_FIELD, "Component Role Hint", 30);
needsUpdate |= bclass.addStaticListField(COMPONENT_SCOPE_FIELD, "Component Scope", 1, false,
"wiki=Current Wiki|user=Current User|global=Global", "select");
if (needsUpdate) {
this.update(doc);
}
}
/**
* Verify that the {@link #DEPENDENCY_CLASS} exists and is up-to-date (act if not).
*
* @throws XWikiException on failure
*/
private void installOrUpdateComponentRequirementXClass() throws XWikiException
{
XWikiContext xcontext = getXWikiContext();
XWikiDocument doc = xcontext.getWiki().getDocument(DEPENDENCY_CLASS_REFERENCE, xcontext);
BaseClass bclass = doc.getXClass();
bclass.setDocumentReference(doc.getDocumentReference());
boolean needsUpdate = false;
needsUpdate |= this.initializeXClassDocumentMetadata(doc, "Wiki Component Dependency XWiki Class");
needsUpdate |= bclass.addTextField(COMPONENT_ROLE_TYPE_FIELD, "Dependency Role Type", 30);
needsUpdate |= bclass.addTextField(COMPONENT_ROLE_HINT_FIELD, "Dependency Role Hint", 30);
needsUpdate |= bclass.addTextField(DEPENDENCY_BINDING_NAME_FIELD, "Binding name", 30);
if (needsUpdate) {
this.update(doc);
}
}
/**
* Verify that the {@link #METHOD_CLASS} exists and is up-to-date (act if not).
*
* @throws XWikiException on failure
*/
private void installOrUpdateComponentMethodXClass() throws XWikiException
{
XWikiContext xcontext = getXWikiContext();
XWikiDocument doc = xcontext.getWiki().getDocument(METHOD_CLASS_REFRENCE, xcontext);
BaseClass bclass = doc.getXClass();
bclass.setDocumentReference(doc.getDocumentReference());
boolean needsUpdate = false;
needsUpdate |= this.initializeXClassDocumentMetadata(doc, "Wiki Component Method XWiki Class");
needsUpdate |= bclass.addTextField(METHOD_NAME_FIELD, "Method name", 30);
// This field contains wiki syntax (usually some scripting macr) but it's technical content that should be
// edited with a Text editor.
needsUpdate |= bclass.addTextAreaField(METHOD_CODE_FIELD, "Method body code", 40, 20,
TextAreaClass.EditorType.TEXT, TextAreaClass.ContentType.WIKI_TEXT);
if (needsUpdate) {
this.update(doc);
}
}
/**
* Utility method for updating a wiki macro class definition document.
*
* @param doc xwiki document containing the wiki macro class.
* @throws XWikiException if an error occurs while saving the document.
*/
private void update(XWikiDocument doc) throws XWikiException
{
XWikiContext xcontext = getXWikiContext();
xcontext.getWiki().saveDocument(doc, xcontext);
}
/**
* Helper method to prepare a document that will hold an XClass definition, setting its initial metadata, if needed
* (author, title, parent, content, etc.).
*
* @param doc the document to prepare
* @param title the title to set
* @return true if the doc has been modified and needs saving, false otherwise
*/
private boolean initializeXClassDocumentMetadata(XWikiDocument doc, String title)
{
boolean needsUpdate = false;
if (doc.getCreatorReference() == null) {
needsUpdate = true;
doc.setCreator(CLASS_AUTHOR);
}
if (doc.getAuthorReference() == null) {
needsUpdate = true;
doc.setAuthorReference(doc.getCreatorReference());
}
if (doc.getParentReference() == null) {
needsUpdate = true;
doc.setParentReference(new EntityReference("XWikiClasses", EntityType.DOCUMENT)
.appendParent(new EntityReference("XWiki", EntityType.SPACE)));
}
if (StringUtils.isBlank(doc.getTitle())) {
needsUpdate = true;
doc.setTitle(title);
}
if (StringUtils.isBlank(doc.getContent()) || !Syntax.XWIKI_2_0.equals(doc.getSyntax())) {
needsUpdate = true;
doc.setContent("{{include reference=\"XWiki.ClassSheet\" /}}");
doc.setSyntax(Syntax.XWIKI_2_0);
}
if (!doc.isHidden()) {
needsUpdate = true;
doc.setHidden(true);
}
return needsUpdate;
}
/**
* @return the XWikiContext extracted from the execution.
*/
private XWikiContext getXWikiContext()
{
return (XWikiContext) this.execution.getContext().getProperty("xwikicontext");
}
}