diff --git a/celements-xapp/component/pom.xml b/celements-xapp/component/pom.xml new file mode 100644 index 000000000..483e79e34 --- /dev/null +++ b/celements-xapp/component/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + com.celements + celements + 6.5-SNAPSHOT + + celements-xapp + 6.0-SNAPSHOT + Celements XApp + + + com.celements + celements-model + 6.6-SNAPSHOT + provided + + + javax.servlet + javax.servlet-api + test + + + + scm:git:git@github.com:celements/celements-features.git + scm:git:git@github.com:celements/celements-features.git + https://github.com/celements/celements-features/celements-xapp/component + HEAD + + diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManager.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManager.java new file mode 100644 index 000000000..cdc40fc43 --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManager.java @@ -0,0 +1,392 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager; + +import java.util.Collection; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.xpn.xwiki.XWiki; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.objects.BaseObject; +import com.xpn.xwiki.objects.classes.ListClass; +import com.xpn.xwiki.plugin.applicationmanager.core.plugin.XWikiPluginMessageTool; +import com.xpn.xwiki.plugin.applicationmanager.doc.XWikiApplication; +import com.xpn.xwiki.plugin.applicationmanager.doc.XWikiApplicationClass; + +/** + * Hidden toolkit used by the plugin API that make all the plugins actions. + * + * @version $Id$ + */ +public final class ApplicationManager +{ + /** + * The logging tool. + */ + protected static final Log LOG = LogFactory.getLog(ApplicationManager.class); + + /** + * Wiki preferences document and class full name. + */ + private static final String XWIKIPREFERENCES = "XWiki.XWikiPreferences"; + + /** + * "documentBundles" list field name of the XWiki.XWikiPreferences class. + */ + private static final String XWIKIPREFERENCES_DOCUMENTBUNDLES = "documentBundles"; + + /** + * "documentBundles" list field separator of the XWiki.XWikiPreferences class. + */ + private static final String XWIKIPREFERENCES_DOCUMENTBUNDLES_SEP = ","; + + /** + * The message tool to use to generate error or comments. + */ + private XWikiPluginMessageTool messageTool; + + // //////////////////////////////////////////////////////////////////////////// + + /** + * @param messageTool the message tool + */ + public ApplicationManager(XWikiPluginMessageTool messageTool) + { + this.messageTool = messageTool; + } + + /** + * Get the {@link XWikiPluginMessageTool} to use with ApplicationManager. + * + * @param context the XWiki context. + * @return a translated strings manager. + */ + public XWikiPluginMessageTool getMessageTool(XWikiContext context) + { + return this.messageTool != null ? this.messageTool : ApplicationManagerMessageTool.getDefault(context); + } + + // //////////////////////////////////////////////////////////////////////////// + // Applications management + + /** + * Get the current wiki root application. + * + * @param context the XWiki context. + * @return the root application descriptor document. If can't find root application return null. + * @throws XWikiException error when getting root application descriptor document from database. + */ + public XWikiApplication getRootApplication(XWikiContext context) throws XWikiException + { + XWiki xwiki = context.getWiki(); + + String docFullName = xwiki.getXWikiPreference("rootapplication", null, context); + + if (docFullName != null) { + XWikiDocument doc = xwiki.getDocument(docFullName, context); + + if (!doc.isNew()) { + return XWikiApplicationClass.getInstance(context).newXObjectDocument(doc, 0, context); + } + } + + return null; + } + + /** + * Search for all document containing a object of class XWikiApplicationClass. + * + * @param context the XWiki context. + * @return a list if {@link XWikiApplication}. + * @throws XWikiException error when searching documents. + */ + public List getApplicationList(XWikiContext context) throws XWikiException + { + return XWikiApplicationClass.getInstance(context, false).searchXObjectDocuments(context); + } + + /** + * Create a new application descriptor base on provided application descriptor. + * + * @param userAppSuperDoc appXObjectDocument the user application descriptor from which new descriptor will be + * created. + * @param failOnExist if true fail if the application descriptor to create already exists. + * @param comment a comment used when saving application descriptor document. + * @param context the XWiki Context. + * @throws XWikiException error when calling for {@link XWiki#getDocument(String, XWikiContext)} + */ + public void createApplication(XWikiApplication userAppSuperDoc, boolean failOnExist, String comment, + XWikiContext context) throws XWikiException + { + XWiki xwiki = context.getWiki(); + XWikiApplicationClass appClass = XWikiApplicationClass.getInstance(context); + + // Verify is server page already exist + XWikiDocument docToSave = + xwiki.getDocument(appClass.getItemDocumentDefaultFullName(userAppSuperDoc.getAppName(), context), context); + + if (!docToSave.isNew() && appClass.isInstance(docToSave)) { + // If we are not allowed to continue if server page already exists + if (failOnExist) { + if (LOG.isErrorEnabled()) { + LOG.error(getMessageTool(context).get(ApplicationManagerMessageTool.ERROR_APPPAGEALREADYEXISTS, + userAppSuperDoc.getAppName())); + } + + throw new ApplicationManagerException(ApplicationManagerException.ERROR_AM_APPDOCALREADYEXISTS, + getMessageTool(context).get(ApplicationManagerMessageTool.ERROR_APPPAGEALREADYEXISTS, + userAppSuperDoc.getAppName())); + } else if (LOG.isWarnEnabled()) { + LOG.warn(getMessageTool(context).get(ApplicationManagerMessageTool.ERROR_APPPAGEALREADYEXISTS, + userAppSuperDoc.getAppName())); + } + + } + + XWikiApplication appSuperDocToSave = + XWikiApplicationClass.getInstance(context).newXObjectDocument(docToSave, 0, context); + + appSuperDocToSave.mergeObject(userAppSuperDoc); + + appSuperDocToSave.save(comment); + + // Update user document with the new document name + userAppSuperDoc.setFullName(appSuperDocToSave.getFullName()); + } + + /** + * Delete an application descriptor document. + * + * @param appName the name of the application. + * @param context the XWiki context. + * @throws XWikiException error when calling for {@link XWikiApplication#delete()} + */ + public void deleteApplication(String appName, XWikiContext context) throws XWikiException + { + XWikiApplication app = getApplication(appName, context, true); + + app.delete(); + } + + /** + * Get the application descriptor document of the provided application name. + * + * @param appName the name of the application. + * @param context the XWiki context. + * @param validate indicate if it return new XWikiDocument or throw exception if application descriptor does not + * exist. + * @return the XWikiApplication representing application descriptor. + * @throws XWikiException error when searching for application descriptor document. + */ + public XWikiApplication getApplication(String appName, XWikiContext context, boolean validate) + throws XWikiException + { + return XWikiApplicationClass.getInstance(context, false).getApplication(appName, validate, context); + } + + /** + * Reload xwiki application. It means : + * + * + * @param app the application to reload. + * @param comment the comment to use when saving documents. + * @param context the XWiki context. + * @throws XWikiException error when : + * + */ + public void reloadApplication(XWikiApplication app, String comment, XWikiContext context) throws XWikiException + { + updateApplicationTranslation(app, comment, context); + } + + /** + * Reload all xwiki applications. It means : + * + * + * @param comment the comment to use when saving documents. + * @param context the XWiki context. + * @throws XWikiException error when : + * + */ + public void reloadAllApplications(String comment, XWikiContext context) throws XWikiException + { + List applist = getApplicationList(context); + + for (XWikiApplication app : applist) { + updateApplicationTranslation(app, comment, context); + } + } + + /** + * Insert in XWiki.XWikiPreferences "documentBundles" field the translation documents of all applications in the + * context's wiki. + * + * @param applications the applications for which to update translations informations. + * @param comment a comment used when saving XWiki. + * @param context the XWiki context. + * @throws XWikiException error when : + * + */ + public void updateApplicationsTranslation(Collection applications, String comment, + XWikiContext context) throws XWikiException + { + XWiki xwiki = context.getWiki(); + + XWikiDocument prefsDoc = xwiki.getDocument(XWIKIPREFERENCES, context); + BaseObject prefsObject = prefsDoc.getObject(XWIKIPREFERENCES); + + if (prefsObject != null) { + String documentBundles = prefsObject.getStringValue(XWIKIPREFERENCES_DOCUMENTBUNDLES); + List translationPrefs = + ListClass.getListFromString(documentBundles, XWIKIPREFERENCES_DOCUMENTBUNDLES_SEP, true); + + boolean updateprefs = false; + + for (XWikiApplication app : applications) { + updateprefs |= updateApplicationTranslation(translationPrefs, app); + } + + if (updateprefs) { + prefsObject.setStringValue(XWIKIPREFERENCES_DOCUMENTBUNDLES, StringUtils.join( + translationPrefs.toArray(), XWIKIPREFERENCES_DOCUMENTBUNDLES_SEP)); + xwiki.saveDocument(prefsDoc, comment, context); + } + } + } + + /** + * Insert in XWiki.XWikiPreferences "documentBundles" field the translation documents of all applications in the + * context's wiki. + * + * @param context the XWiki context. + * @throws XWikiException error when : + *
    + *
  • getting wiki preferences document.
  • + *
  • or searching for all applications in the wiki.
  • + *
  • or saving wiki preferences document.
  • + *
+ */ + public void updateAllApplicationTranslation(XWikiContext context) throws XWikiException + { + updateApplicationsTranslation(getApplicationList(context), getMessageTool(context).get( + ApplicationManagerMessageTool.COMMENT_REFRESHALLTRANSLATIONS), context); + } + + /** + * Insert in XWiki.XWikiPreferences "documentBundles" field the translation documents of all applications in the + * context's wiki. + * + * @param document the document containing the applications descriptors + * @param context the XWiki context. + * @throws XWikiException error when : + *
    + *
  • getting wiki preferences document.
  • + *
  • or searching for all applications in the wiki.
  • + *
  • or saving wiki preferences document.
  • + *
+ * @since 1.9 + */ + public void updateApplicationsTranslation(XWikiDocument document, XWikiContext context) throws XWikiException + { + List appList = + XWikiApplicationClass.getInstance(context).newXObjectDocumentList(document, context); + updateApplicationsTranslation(appList, getMessageTool(context).get( + ApplicationManagerMessageTool.COMMENT_AUTOUPDATETRANSLATIONS, document.getFullName()), context); + } + + /** + * Insert in XWiki.XWikiPreferences "documentBundles" field the translation documents of the provided application. + * + * @param app the application descriptor. + * @param comment a comment used when saving XWiki. + * @param context the XWiki context. + * @throws XWikiException error when : + *
    + *
  • getting wiki preferences document.
  • + *
  • or saving wiki preferences document.
  • + *
+ */ + public void updateApplicationTranslation(XWikiApplication app, String comment, XWikiContext context) + throws XWikiException + { + XWiki xwiki = context.getWiki(); + + XWikiDocument prefsDoc = xwiki.getDocument(XWIKIPREFERENCES, context); + BaseObject prefsObject = prefsDoc.getObject(XWIKIPREFERENCES); + + if (prefsObject != null) { + String documentBundles = prefsObject.getStringValue(XWIKIPREFERENCES_DOCUMENTBUNDLES); + List translationPrefs = + ListClass.getListFromString(documentBundles, XWIKIPREFERENCES_DOCUMENTBUNDLES_SEP, true); + + boolean updateprefs = updateApplicationTranslation(translationPrefs, app); + + if (updateprefs) { + prefsObject.setStringValue(XWIKIPREFERENCES_DOCUMENTBUNDLES, StringUtils.join( + translationPrefs.toArray(), XWIKIPREFERENCES_DOCUMENTBUNDLES_SEP)); + xwiki.saveDocument(prefsDoc, comment, context); + } + } + } + + /** + * Insert in translationPrefs the translation documents of the provided application. + * + * @param translationPrefs the list of translation documents to complete. + * @param app the application's descriptor. + * @return true if at least one document has been inserted in translationPrefs. + */ + public boolean updateApplicationTranslation(List translationPrefs, XWikiApplication app) + { + boolean updateprefs = false; + + List translationDocs = app.getTranslationDocs(); + for (String translationDoc : translationDocs) { + if (!translationPrefs.contains(translationDoc)) { + translationPrefs.add(translationDoc); + updateprefs = true; + } + } + + return updateprefs; + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerException.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerException.java new file mode 100644 index 000000000..89da3f247 --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerException.java @@ -0,0 +1,109 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager; + +import com.xpn.xwiki.plugin.PluginException; + +/** + * Application Manager plugin base exception. + * + * @version $Id$ + */ +public class ApplicationManagerException extends PluginException +{ + /** + * Application Manager plugin error identifier. + */ + public static final int MODULE_PLUGIN_APPLICATIONMANAGER = 60; + + /** + * Error when trying to create application descriptor that already exist in the database. + */ + public static final int ERROR_AM_APPDOCALREADYEXISTS = 60010; + + /** + * Error when trying to get application descriptor that does not exist in the database. + */ + public static final int ERROR_AM_DOESNOTEXIST = 60011; + + /** + * The default ApplicationManagerException. + */ + private static final ApplicationManagerException DEFAULT_EXCEPTION = new ApplicationManagerException(); + + // ////// + + /** + * Create an ApplicationManagerException. + * + * @param code the error code. + * @param message a literal message about this error. + */ + public ApplicationManagerException(int code, String message) + { + super(ApplicationManagerPlugin.class, code, message); + } + + /** + * Create an ApplicationManagerException. Replace any parameters found in the message by the passed + * args parameters. The format is the one used by {@link java.text.MessageFormat}. + * + * @param code the error code. + * @param message a literal message about this error. + * @param e the exception this exception wrap. + * @param args the array of parameters to use for replacing "{N}" elements in the string. See + * {@link java.text.MessageFormat} for the full syntax + */ + public ApplicationManagerException(int code, String message, Throwable e, Object[] args) + { + super(ApplicationManagerPlugin.class, code, message, e, args); + } + + /** + * Create an ApplicationManagerException. + * + * @param code the error code. + * @param message a literal message about this error. + * @param e the exception this exception wrap. + */ + public ApplicationManagerException(int code, String message, Throwable e) + { + super(ApplicationManagerPlugin.class, code, message, e); + } + + // ////// + + /** + * Create default ApplicationManagerException. + */ + private ApplicationManagerException() + { + super(ApplicationManagerPlugin.class, 0, "No error"); + } + + /** + * @return unique instance of the default ApplicationManagerException. + */ + public static ApplicationManagerException getDefaultException() + { + return DEFAULT_EXCEPTION; + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerMessageTool.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerMessageTool.java new file mode 100644 index 000000000..63c2a8e6e --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerMessageTool.java @@ -0,0 +1,204 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager; + +import java.util.Locale; +import java.util.ResourceBundle; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.plugin.applicationmanager.core.plugin.XWikiPluginMessageTool; + +/** + * Application Manager plugin translation messages manager. + *

+ * The main use of this class is construct {@link XWikiPluginMessageTool} with the correct + * {@link java.util.ResourceBundle} and to list all the message keys used internally in the plugin. + * + * @version $Id$ + * @since 1.1 + */ +public class ApplicationManagerMessageTool extends XWikiPluginMessageTool +{ + /** + * Key to use with {@link XWikiContext#get(Object)}. + */ + public static final String MESSAGETOOL_CONTEXT_KEY = "applicationmanagermessagetool"; + + /** + * Used as comment when creating a new application. + */ + public static final String COMMENT_CREATEAPPLICATION = "applicationmanager.plugin.comment.createapplication"; + + /** + * Used as comment when importing a new application. + */ + public static final String COMMENT_IMPORTAPPLICATION = "applicationmanager.plugin.comment.importapplication"; + + /** + * Used as comment when reloading an application. + */ + public static final String COMMENT_RELOADAPPLICATION = "applicationmanager.plugin.comment.reloadapplication"; + + /** + * Used as comment when reloading all applications. + */ + public static final String COMMENT_RELOADALLAPPLICATIONS = + "applicationmanager.plugin.comment.reloadallapplications"; + + /** + * Used as comment when automatically update application translations pages. + */ + public static final String COMMENT_AUTOUPDATETRANSLATIONS = + "applicationmanager.plugin.comment.autoupdatetranslations"; + + /** + * Used as comment when refreshing all applications translations pages. + */ + public static final String COMMENT_REFRESHALLTRANSLATIONS = + "applicationmanager.plugin.comment.refreshalltranslations"; + + /** + * Used as {@link ApplicationManagerException} message when application default page name already exists. + */ + public static final String ERROR_APPPAGEALREADYEXISTS = + "applicationmanager.plugin.error.applicationpagealreadyexists"; + + /** + * Used as {@link ApplicationManagerException} message when provided XAR package does not exists. + */ + public static final String ERROR_IMORT_PKGDOESNOTEXISTS = + "applicationmanager.plugin.error.import.packagedoesnotexists"; + + /** + * Used as {@link ApplicationManagerException} message when failed to load XAR package as list of + * {@link com.xpn.xwiki.doc.XWikiDocument}. + */ + public static final String ERROR_IMORT_IMPORT = "applicationmanager.plugin.error.import.import"; + + /** + * Used as {@link ApplicationManagerException} message when failed to insert loaded + * {@link com.xpn.xwiki.doc.XWikiDocument} from package into database. + */ + public static final String ERROR_IMORT_INSTALL = "applicationmanager.plugin.error.import.install"; + + /** + * Used as {@link ApplicationManagerException} message when failed to find application from provided application + * name. + */ + public static final String ERROR_APPDOESNOTEXISTS = "applicationmanager.plugin.error.applicationdoesnotexists"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when application creation failed. + */ + public static final String LOG_CREATEAPP = "applicationmanager.plugin.log.createapplication"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when application delete failed. + */ + public static final String LOG_DELETEAPP = "applicationmanager.plugin.log.deleteapplication"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when getting all application descriptors failed. + */ + public static final String LOG_GETALLAPPS = "applicationmanager.plugin.log.getallapplications"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when getting application descriptor failed. + */ + public static final String LOG_GETAPP = "applicationmanager.plugin.log.getapplication"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when exporting application failed. + */ + public static final String LOG_EXPORTAPP = "applicationmanager.plugin.log.exportapplication"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when importing application failed. + */ + public static final String LOG_IMPORTAPP = "applicationmanager.plugin.log.importapplication"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when reloading application failed. + */ + public static final String LOG_RELOADAPP = "applicationmanager.plugin.log.reloadapplication"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when reloading all applications failed. + */ + public static final String LOG_REALOADALLAPPS = "applicationmanager.plugin.log.realoadallapplications"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when automatically updating application translations + * informations failed. + */ + public static final String LOG_AUTOUPDATETRANSLATIONS = "applicationmanager.plugin.log.autoupdatetranslations"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when refreshing all applications translations pages. + */ + public static final String LOG_REFRESHALLTRANSLATIONS = "applicationmanager.plugin.log.refreshalltranslations"; + + /** + * Used as {@link org.apache.commons.logging.Log} log message when getting wiki root application failed. + */ + public static final String LOG_GETROOTAPP = "applicationmanager.plugin.log.getrootapplication"; + + /** + * Default bundle manager where to find translated messages. + */ + private static final ApplicationManagerMessageTool DEFAULTMESSAGETOOL = new ApplicationManagerMessageTool(); + + /** + * Create default WikiManagerMessageTool. Only look at WikiManager properties file with system {@link Locale}. + */ + private ApplicationManagerMessageTool() + { + super(ResourceBundle.getBundle(ApplicationManagerPlugin.PLUGIN_NAME + "/ApplicationResources")); + } + + /** + * Call for {@link XWikiPluginMessageTool#XWikiPluginMessageTool(ResourceBundle, XWikiContext)}. Construct + * ResourceBundle based on {@link WikiManagerPlugin#PLUGIN_NAME} + "/ApplicationResources". + * + * @param locale the {@link Locale} used to load the {@link ResourceBundle}. + * @param plugin the plugin. + * @param context the {@link com.xpn.xwiki.XWikiContext} object, used to get access to XWiki primitives for loading + * documents + */ + ApplicationManagerMessageTool(Locale locale, ApplicationManagerPlugin plugin, XWikiContext context) + { + super(locale, plugin, context); + } + + /** + * Get Wiki Manager message tool registered in XWiki context. If not return default. + * + * @param context the XWiki context from which to get message tool. + * @return the default Wiki Manager message tool. + */ + public static ApplicationManagerMessageTool getDefault(XWikiContext context) + { + Object messagetool = context.get(MESSAGETOOL_CONTEXT_KEY); + + return messagetool != null && messagetool instanceof ApplicationManagerMessageTool + ? (ApplicationManagerMessageTool) messagetool : DEFAULTMESSAGETOOL; + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerPlugin.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerPlugin.java new file mode 100644 index 000000000..170db12e4 --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerPlugin.java @@ -0,0 +1,174 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.xwiki.bridge.event.DocumentCreatedEvent; +import org.xwiki.bridge.event.DocumentUpdatedEvent; +import org.xwiki.observation.EventListener; +import org.xwiki.observation.ObservationManager; +import org.xwiki.observation.event.Event; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.api.Api; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.plugin.XWikiDefaultPlugin; +import com.xpn.xwiki.plugin.XWikiPluginInterface; +import com.xpn.xwiki.plugin.applicationmanager.doc.XWikiApplicationClass; +import com.xpn.xwiki.web.Utils; +import com.xpn.xwiki.web.XWikiURLFactory; + +/** + * Entry point of the Application Manager plugin. + * + * @version $Id$ + */ +public class ApplicationManagerPlugin extends XWikiDefaultPlugin implements EventListener +{ + /** + * Identifier of Application Manager plugin. + */ + public static final String PLUGIN_NAME = "applicationmanager"; + + // //////////////////////////////////////////////////////////////////////////// + + /** + * The logging tool. + */ + protected static final Log LOG = LogFactory.getLog(ApplicationManagerPlugin.class); + + /** + * The events matchers. + */ + private static final List EVENTS = new ArrayList() + { + { + add(new DocumentUpdatedEvent()); + add(new DocumentCreatedEvent()); + } + }; + + /** + * Protected API for managing applications. + */ + private ApplicationManager applicationManager; + + // //////////////////////////////////////////////////////////////////////////// + + /** + * Construction the entry point of the Application Manager plugin. + * + * @param name the identifier of the plugin. + * @param className the class name of the entry point of the plugin. + * @param context the XWiki context. + */ + public ApplicationManagerPlugin(String name, String className, XWikiContext context) + { + super(name, className, context); + } + + /** + * {@inheritDoc} + * + * @see org.xwiki.observation.EventListener#getEvents() + */ + public List getEvents() + { + return EVENTS; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.XWikiDefaultPlugin#init(com.xpn.xwiki.XWikiContext) + */ + @Override + public void init(XWikiContext context) + { + this.applicationManager = new ApplicationManager(ApplicationManagerMessageTool.getDefault(context)); + + // register for documents modifications events + Utils.getComponent(ObservationManager.class).addListener(this); + + String database = context.getDatabase(); + try { + XWikiURLFactory urlf = + context.getWiki().getURLFactoryService().createURLFactory(context.getMode(), context); + context.setURLFactory(urlf); + context.setDatabase(context.getMainXWiki()); + this.applicationManager.updateAllApplicationTranslation(context); + } catch (XWikiException e) { + LOG.error(ApplicationManagerMessageTool.getDefault(context).get( + ApplicationManagerMessageTool.LOG_REFRESHALLTRANSLATIONS), e); + } finally { + context.setDatabase(database); + } + } + + /** + * {@inheritDoc} + * + * @see org.xwiki.observation.EventListener#onEvent(org.xwiki.observation.event.Event, java.lang.Object, + * java.lang.Object) + */ + public void onEvent(Event event, Object source, Object data) + { + XWikiDocument document = (XWikiDocument) source; + XWikiContext context = (XWikiContext) data; + + try { + if (XWikiApplicationClass.isApplication(document)) { + this.applicationManager.updateApplicationsTranslation(document, context); + } + } catch (XWikiException e) { + LOG.error(ApplicationManagerMessageTool.getDefault(context).get( + ApplicationManagerMessageTool.LOG_AUTOUPDATETRANSLATIONS, document.getFullName()), e); + } + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.XWikiDefaultPlugin#getName() + */ + @Override + public String getName() + { + return PLUGIN_NAME; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.XWikiDefaultPlugin#getPluginApi(com.xpn.xwiki.plugin.XWikiPluginInterface, + * com.xpn.xwiki.XWikiContext) + */ + @Override + public Api getPluginApi(XWikiPluginInterface plugin, XWikiContext context) + { + return new ApplicationManagerPluginApi((ApplicationManagerPlugin) plugin, context); + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerPluginApi.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerPluginApi.java new file mode 100644 index 000000000..4e3a6f2b9 --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationManagerPluginApi.java @@ -0,0 +1,439 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import com.xpn.xwiki.plugin.applicationmanager.core.api.XWikiExceptionApi; +import com.xpn.xwiki.plugin.PluginApi; +import com.xpn.xwiki.plugin.applicationmanager.doc.XWikiApplication; +import com.xpn.xwiki.plugin.applicationmanager.doc.XWikiApplicationClass; +import com.xpn.xwiki.web.XWikiMessageTool; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Plugin for managing applications: installation, export, creation. The plugin uses the concept of an Application + * Descriptor describing an application (its version, the documents it contains, the translations, etc). + * + * @version $Id$ + * @see com.xpn.xwiki.plugin.applicationmanager.ApplicationManagerPlugin + */ +public class ApplicationManagerPluginApi extends PluginApi +{ + /** + * Field name of the last error code inserted in context. + */ + public static final String CONTEXT_LASTERRORCODE = "lasterrorcode"; + + /** + * Field name of the last api exception inserted in context. + */ + public static final String CONTEXT_LASTEXCEPTION = "lastexception"; + + /** + * The logging tool. + */ + protected static final Log LOG = LogFactory.getLog(ApplicationManagerPluginApi.class); + + /** + * The default ApplicationManager managed exception. + */ + private XWikiExceptionApi defaultException; + + /** + * Protected API for managing applications. + */ + private ApplicationManager applicationManager; + + /** + * Protected API for installing/exporting applications. + */ + private ApplicationPackager applicationPackager; + + /** + * The plugin internationalization service. + */ + private ApplicationManagerMessageTool messageTool; + + /** + * Create an instance of the Application Manager plugin user api. + * + * @param plugin the entry point of the Application Manager plugin. + * @param context the XWiki context. + */ + public ApplicationManagerPluginApi(ApplicationManagerPlugin plugin, XWikiContext context) + { + super(plugin, context); + + // Default Exception + this.defaultException = new XWikiExceptionApi(ApplicationManagerException.getDefaultException(), context); + + // Message Tool + Locale locale = (Locale) context.get("locale"); + this.messageTool = new ApplicationManagerMessageTool(locale, plugin, context); + context.put(ApplicationManagerMessageTool.MESSAGETOOL_CONTEXT_KEY, this.messageTool); + + this.applicationManager = new ApplicationManager(this.messageTool); + this.applicationPackager = new ApplicationPackager(this.messageTool); + } + + /** + * @return the default plugin api exception. + */ + public XWikiExceptionApi getDefaultException() + { + return this.defaultException; + } + + /** + * @return the plugin internationalization service. + */ + public XWikiMessageTool getMessageTool() + { + return this.messageTool; + } + + /** + * Log error and store details in the context. + * + * @param errorMessage error message. + * @param e the catched exception. + */ + public void logError(String errorMessage, XWikiException e) + { + LOG.error(errorMessage, e); + + context.put(CONTEXT_LASTERRORCODE, Integer.valueOf(e.getCode())); + context.put(CONTEXT_LASTEXCEPTION, new XWikiExceptionApi(e, context)); + } + + // //////////////////////////////////////////////////////////////////////////// + // Applications management + + /** + * Create empty application document. + * + * @return an empty application descriptor document. + * @throws XWikiException all error that does not caused by user of this method. + */ + public XWikiApplication createApplicationDocument() throws XWikiException + { + return XWikiApplicationClass.getInstance(context).newXObjectDocument(context); + } + + /** + * Create a new application descriptor base on provided application descriptor. + * + * @param appXObjectDocument the user application descriptor from which new descriptor will be created. + * @param failOnExist if true fail if the application descriptor to create already exists. + * @return error code . If there is error, it add error code in context {@link #CONTEXT_LASTERRORCODE} field and + * exception in context's {@link #CONTEXT_LASTEXCEPTION} field. + *

+ * Error codes can be : + *

    + *
  • {@link XWikiExceptionApi#ERROR_NOERROR} : method succeed with no error.
  • + *
  • {@link XWikiException#ERROR_XWIKI_ACCESS_DENIED} : context's user don't have rights to do this + * action.
  • + *
  • {@link ApplicationManagerException#ERROR_AM_APPDOCALREADYEXISTS} : application descriptor already + * exists.
  • + *
+ * @throws XWikiException all error that does not caused by user of this method. + */ + public int createApplication(XWikiApplication appXObjectDocument, boolean failOnExist) throws XWikiException + { + int returncode = XWikiExceptionApi.ERROR_NOERROR; + + try { + this.applicationManager.createApplication(appXObjectDocument, failOnExist, this.messageTool.get( + ApplicationManagerMessageTool.COMMENT_CREATEAPPLICATION, appXObjectDocument.toString()), context); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_CREATEAPP, appXObjectDocument.toString()), + e); + + returncode = e.getCode(); + } + + return returncode; + } + + /** + * Delete an application descriptor document. + * + * @param appName the name of the application. + * @return error code . If there is error, it add error code in context {@link #CONTEXT_LASTERRORCODE} field and + * exception in context's {@link #CONTEXT_LASTEXCEPTION} field. + *

+ * Error codes can be : + *

    + *
  • {@link XWikiExceptionApi#ERROR_NOERROR} : action finished with no error. + *
  • {@link XWikiException#ERROR_XWIKI_ACCESS_DENIED} : context's user don't have rights to do this + * action. + *
  • {@link ApplicationManagerException#ERROR_AM_DOESNOTEXIST} : provided application does not exist. + *
+ * @throws XWikiException all error that does not caused by user of this method. + */ + public int deleteApplication(String appName) throws XWikiException + { + int returncode = XWikiExceptionApi.ERROR_NOERROR; + + try { + this.applicationManager.deleteApplication(appName, context); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_DELETEAPP, appName), e); + + returncode = e.getCode(); + } + + return returncode; + } + + /** + * Get all applications descriptors documents. + * + * @return a list of XWikiApplication. + * @throws XWikiException all error that does not caused by user of this method. + */ + public List getApplicationDocumentList() throws XWikiException + { + List listDocument = Collections.emptyList(); + + try { + listDocument = this.applicationManager.getApplicationList(this.context); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_GETALLAPPS), e); + } + + return listDocument; + } + + /** + * Get the application descriptor document of the provided application name. + * + * @param appName the name of the application. + * @return the application descriptor document. If there is error, it add error code in context + * {@link #CONTEXT_LASTERRORCODE} field and exception in context's {@link #CONTEXT_LASTEXCEPTION} field. + * Error codes can be : + *
    + *
  • {@link ApplicationManagerException#ERROR_AM_DOESNOTEXIST} : provided application does not exist. + *
+ * @throws XWikiException all error that does not caused by user of this method. + */ + public XWikiApplication getApplicationDocument(String appName) throws XWikiException + { + XWikiApplication app = null; + + try { + app = this.applicationManager.getApplication(appName, context, true); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_GETAPP, appName), e); + } + + return app; + } + + /** + * Export an application into XAR using Packaging plugin. + * + * @param appName the name of the application to export. + * @return error code . If there is error, it add error code in context {@link #CONTEXT_LASTERRORCODE} field and + * exception in context's {@link #CONTEXT_LASTEXCEPTION} field. + *

+ * Error codes can be : + *

    + *
  • {@link XWikiExceptionApi#ERROR_NOERROR} : action finished with no error. + *
  • {@link ApplicationManagerException#ERROR_AM_DOESNOTEXIST} : provided application does not exist. + *
+ * @throws XWikiException all error that does not caused by user of this method. + * @throws IOException all error that does not caused by user of this method. + */ + public int exportApplicationXAR(String appName) throws XWikiException, IOException + { + return exportApplicationXAR(appName, true, false); + } + + /** + * Export an application into XAR using Packaging plugin. + * + * @param appName the name of the application. + * @param recurse if true include all dependencies applications into XAR. + * @param withDocHistory if true export with documents history. + * @return error code . If there is error, it add error code in context {@link #CONTEXT_LASTERRORCODE} field and + * exception in context's {@link #CONTEXT_LASTEXCEPTION} field. + *

+ * Error codes can be : + *

    + *
  • {@link XWikiExceptionApi#ERROR_NOERROR} : action finished with no error. + *
  • {@link ApplicationManagerException#ERROR_AM_DOESNOTEXIST} : provided application does not exist. + *
+ * @throws XWikiException all error that does not caused by user of this method. + * @throws IOException all error that does not caused by user of this method. + */ + public int exportApplicationXAR(String appName, boolean recurse, boolean withDocHistory) throws XWikiException, + IOException + { + int returncode = XWikiExceptionApi.ERROR_NOERROR; + + try { + this.applicationPackager.exportApplicationXAR(appName, recurse, withDocHistory, context); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_EXPORTAPP, appName), e); + + returncode = e.getCode(); + } + + return returncode; + } + + /** + * Import attached application XAR into current wiki and do all actions needed to installation an application. See + * {@link #reloadApplication(String)} for more. + * + * @param packageName the name of the attached XAR file to import. + * @return error code . If there is error, it add error code in context {@link #CONTEXT_LASTERRORCODE} field and + * exception in context's {@link #CONTEXT_LASTEXCEPTION} field. + *

+ * Error codes can be : + *

    + *
  • {@link XWikiExceptionApi#ERROR_NOERROR} : action finished with no error. + *
  • {@link XWikiException#ERROR_XWIKI_ACCESS_DENIED} : context's user don't have rights to do this + * action. + *
  • {@link ApplicationManagerException#ERROR_AM_DOESNOTEXIST} : provided application does not exist. + *
+ * @throws XWikiException all error that does not caused by user of this method. + */ + public int importApplication(String packageName) throws XWikiException + { + if (!hasAdminRights()) { + return XWikiException.ERROR_XWIKI_ACCESS_DENIED; + } + + int returncode = XWikiExceptionApi.ERROR_NOERROR; + + try { + this.applicationPackager.importApplication(context.getDoc(), packageName, this.messageTool.get( + ApplicationManagerMessageTool.COMMENT_IMPORTAPPLICATION, packageName), context); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_IMPORTAPP, packageName), e); + + returncode = e.getCode(); + } + + return returncode; + } + + /** + * Reload xwiki application. It means : + *
    + *
  • update XWikiPreferences with application translation documents. + *
+ * + * @param appName the name of the application to reload. + * @return error code . If there is error, it add error code in context {@link #CONTEXT_LASTERRORCODE} field and + * exception in context's {@link #CONTEXT_LASTEXCEPTION} field. + *

+ * Error codes can be : + *

    + *
  • {@link XWikiExceptionApi#ERROR_NOERROR} : action finished with no error. + *
  • {@link XWikiException#ERROR_XWIKI_ACCESS_DENIED} : context's user don't have rights to do this + * action. + *
  • {@link ApplicationManagerException#ERROR_AM_DOESNOTEXIST} : provided application does not exist. + *
+ * @throws XWikiException all error that does not caused by user of this method. + */ + public int reloadApplication(String appName) throws XWikiException + { + if (!hasAdminRights()) { + return XWikiException.ERROR_XWIKI_ACCESS_DENIED; + } + + int returncode = XWikiExceptionApi.ERROR_NOERROR; + + try { + XWikiApplication app = this.applicationManager.getApplication(appName, context, true); + this.applicationManager.reloadApplication(app, this.messageTool.get( + ApplicationManagerMessageTool.COMMENT_RELOADAPPLICATION, app.getAppName()), context); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_RELOADAPP, appName), e); + + returncode = e.getCode(); + } + + return returncode; + } + + /** + * Reload all xwiki applications. It means : - update XWikiPreferences with each application translation documents + * + * @return error code. + *
    + *
  • {@link XWikiExceptionApi#ERROR_NOERROR} : action finished with no error. + *
  • + * {@link XWikiException#ERROR_XWIKI_ACCESS_DENIED} : context's user don't have rights to do this + * action. + *
+ * @throws XWikiException all error that does not caused by user of this method. + */ + public int reloadAllApplications() throws XWikiException + { + if (!hasAdminRights()) { + return XWikiException.ERROR_XWIKI_ACCESS_DENIED; + } + + int returncode = XWikiExceptionApi.ERROR_NOERROR; + + try { + this.applicationManager.reloadAllApplications( + this.messageTool.get(ApplicationManagerMessageTool.COMMENT_RELOADALLAPPLICATIONS), context); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_REALOADALLAPPS), e); + + returncode = e.getCode(); + } + + return returncode; + } + + /** + * Get the current wiki root application. + * + * @return the root application descriptor document. If can't find root application return null. + * @throws XWikiException all error that does not caused by user of this method. + */ + public XWikiApplication getRootApplication() throws XWikiException + { + XWikiApplication app = null; + + try { + app = this.applicationManager.getRootApplication(context); + } catch (ApplicationManagerException e) { + logError(this.messageTool.get(ApplicationManagerMessageTool.LOG_GETROOTAPP), e); + } + + return app; + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationPackager.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationPackager.java new file mode 100644 index 000000000..c85d904ce --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/ApplicationPackager.java @@ -0,0 +1,148 @@ +package com.xpn.xwiki.plugin.applicationmanager; + +import java.io.IOException; +import java.util.Set; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.plugin.applicationmanager.core.plugin.XWikiPluginMessageTool; +import com.xpn.xwiki.plugin.applicationmanager.doc.XWikiApplication; +import com.xpn.xwiki.plugin.applicationmanager.doc.XWikiApplicationClass; +import com.xpn.xwiki.plugin.packaging.DocumentInfo; +import com.xpn.xwiki.plugin.packaging.DocumentInfoAPI; +import com.xpn.xwiki.plugin.packaging.PackageAPI; + +/** + * Provide method to install export applications. + * + * @version $Id$ + * @since 1.9 + */ +public class ApplicationPackager +{ + /** + * The name of the internal packaging plugin. + */ + private static final String PACKAGEPLUGIN_NAME = "package"; + + /** + * The message tool to use to generate error or comments. + */ + private XWikiPluginMessageTool messageTool; + + /** + * Protected API for managing applications. + */ + private ApplicationManager applicationManager; + + // //////////////////////////////////////////////////////////////////////////// + + /** + * @param messageTool the message tool + */ + public ApplicationPackager(XWikiPluginMessageTool messageTool) + { + this.messageTool = messageTool; + + this.applicationManager = new ApplicationManager(this.messageTool); + } + + /** + * Get the {@link XWikiPluginMessageTool} to use with ApplicationManager. + * + * @param context the XWiki context. + * @return a translated strings manager. + */ + public XWikiPluginMessageTool getMessageTool(XWikiContext context) + { + return this.messageTool != null ? this.messageTool : ApplicationManagerMessageTool.getDefault(context); + } + + /** + * Export an application into XAR using Packaging plugin. + * + * @param appName the name of the application to export. + * @param recurse indicate if dependencies applications has to be included in the package. + * @param withDocHistory indicate if history of documents is exported. + * @param context the XWiki context. + * @throws XWikiException error when : + *
    + *
  • getting application descriptor document to export.
  • + *
  • or getting application's documents to export.
  • + *
  • or when apply export.
  • + *
+ * @throws IOException error when apply export. + */ + public void exportApplicationXAR(String appName, boolean recurse, boolean withDocHistory, XWikiContext context) + throws XWikiException, IOException + { + XWikiApplication app = this.applicationManager.getApplication(appName, context, true); + + PackageAPI export = ((PackageAPI) context.getWiki().getPluginApi(PACKAGEPLUGIN_NAME, context)); + + export.setName(app.getAppName() + "-" + app.getAppVersion()); + + Set documents = app.getDocumentsNames(recurse, true); + for (String documentName : documents) { + export.add(documentName, DocumentInfo.ACTION_OVERWRITE); + } + + export.setWithVersions(withDocHistory); + + export.export(); + } + + /** + * Import attached application XAR into current wiki and do all actions needed to installation an application. See + * {@link #reloadApplication(XWikiApplication, String, XWikiContext)} for more. + * + * @param packageDoc the document where package to import is attached. + * @param packageName the name of the attached XAR file to import. + * @param comment a comment used update XWiki.XWikiPreferences. + * @param context the XWiki context. + * @throws XWikiException error when : + *
    + *
  • getting attached package file.
  • + *
  • or load package in memory.
  • + *
  • or installing loaded document in database
  • + *
  • or apply application initialization for each application descriptor document.
  • + *
+ */ + public void importApplication(XWikiDocument packageDoc, String packageName, String comment, XWikiContext context) + throws XWikiException + { + XWikiAttachment packFile = packageDoc.getAttachment(packageName); + + if (packFile == null) { + throw new ApplicationManagerException(XWikiException.ERROR_XWIKI_UNKNOWN, getMessageTool(context).get( + ApplicationManagerMessageTool.ERROR_IMORT_PKGDOESNOTEXISTS, packageName)); + } + + // Import + PackageAPI importer = ((PackageAPI) context.getWiki().getPluginApi(PACKAGEPLUGIN_NAME, context)); + + try { + importer.Import(packFile.getContent(context)); + } catch (IOException e) { + throw new ApplicationManagerException(XWikiException.ERROR_XWIKI_UNKNOWN, getMessageTool(context).get( + ApplicationManagerMessageTool.ERROR_IMORT_IMPORT, packageName), e); + } + + if (importer.install() == DocumentInfo.INSTALL_IMPOSSIBLE) { + throw new ApplicationManagerException(XWikiException.ERROR_XWIKI_UNKNOWN, getMessageTool(context).get( + ApplicationManagerMessageTool.ERROR_IMORT_INSTALL, packageName)); + } + + // Apply applications installation + for (DocumentInfoAPI docinfo : importer.getFiles()) { + XWikiDocument doc = docinfo.getDocInfo().getDoc(); + + if (XWikiApplicationClass.getInstance(context).isInstance(doc)) { + this.applicationManager.reloadApplication( + XWikiApplicationClass.getInstance(context).newXObjectDocument(doc, 0, context), comment, context); + } + } + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/api/XWikiExceptionApi.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/api/XWikiExceptionApi.java new file mode 100644 index 000000000..83fd4b368 --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/api/XWikiExceptionApi.java @@ -0,0 +1,112 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.core.api; + +import java.lang.reflect.Field; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.api.Api; + +/** + * Permit to manipulate XWikiException in velocity code. + * + * @version $Id$ + */ +public class XWikiExceptionApi extends Api +{ + /** + * No error. + */ + public static final int ERROR_NOERROR = -1; + + /** + * Error code that is used when requested error code does not exists. + */ + public static final int ERROR_XWIKI_ERROR_DOES_NOT_EXIST = -2; + + // ///////////////////////////////////////////////////////////: + + /** + * Managed exception. + */ + private XWikiException exception; + + /** + * XWikiExceptionApi constructor. + * + * @param exception the XWiki exception to manage. + * @param context the XWiki context. + */ + public XWikiExceptionApi(XWikiException exception, XWikiContext context) + { + super(context); + this.exception = exception; + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return this.exception.getMessage(); + } + + // ///////////////////////////////////////////////////////////: + + /** + * Get static field error code value. This name targeting velocity to be able to use like + * "exception.SOME_ERROR_CODE". + * + * @param error the static field name. + * @return the static field value. + * @throws XWikiException ERROR_XWIKI_ERROR_DOES_NOT_EXIST No corresponding error code exist. + */ + public int get(String error) throws XWikiException + { + try { + Field field = getClass().getField(error); + + if (field.getType() == int.class) { + return ((Integer) field.get(null)).intValue(); + } + } catch (Exception e) { + // Error code is not a XWikiExceptionApi field. + } + + try { + Field field = this.exception.getClass().getField(error); + + if (field.getType() == int.class) { + return ((Integer) field.get(null)).intValue(); + } + } catch (Exception e) { + // Error when trying to retrieve error code value. + } + + throw new XWikiException(XWikiException.MODULE_XWIKI, + ERROR_XWIKI_ERROR_DOES_NOT_EXIST, + "Error \"" + error + "\" code does not exist"); + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/AbstractXClassManager.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/AbstractXClassManager.java new file mode 100644 index 000000000..f7b8bd1c4 --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/AbstractXClassManager.java @@ -0,0 +1,1075 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.xwiki.model.reference.ClassReference; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.rendering.syntax.Syntax; + +import com.celements.model.access.IModelAccessFacade; +import com.celements.model.access.exception.DocumentSaveException; +import com.celements.model.context.ModelContext; +import com.celements.model.reference.RefBuilder; +import com.celements.model.util.ModelUtils; +import com.celements.model.util.ReferenceSerializationMode; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.api.Document; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.objects.BaseObject; +import com.xpn.xwiki.objects.classes.BaseClass; +import com.xpn.xwiki.objects.classes.BooleanClass; + +/** + * Abstract implementation of XClassManager. + *

+ * This class has to be extended with at least : + *

    + *
  • overload {@link #updateBaseClass(BaseClass)} + *
  • in constructor call AbstractXClassManager constructor with a name that will be used to + * generate all the documents + * and spaces needed. + *
+ * + * @param + * the item class extending {@link XObjectDocument}. + * @version $Id$ + * @see XClassManager + * @since Application Manager 1.0RC1 + */ +public abstract class AbstractXClassManager implements XClassManager { + + /** + * FullName of the default parent page for a document containing xwiki class. + */ + private static final String DEFAULT_XWIKICLASS_PARENT = "XWiki.XWikiClasses"; + + /** + * The resource file extension containing pages contents. + */ + private static final String DOCUMENTCONTENT_EXT = ".vm"; + + /** + * Resource path prefix for the class sheets documents content. + */ + private static final String DOCUMENTCONTENT_SHEET_PREFIX = "sheets/"; + + /** + * Resource path prefix for the class templates documents content. + */ + private static final String DOCUMENTCONTENT_TEMPLATE_PREFIX = "templates/"; + + /** + * Symbol used in HQL request to insert and protect value when executing the request. + */ + private static final String HQL_PARAMETER_STRING = "?"; + + /** + * Space prefix of class document. + * + * @see #getClassSpace() + */ + private final String classSpacePrefix; + + /** + * Prefix of class document. + * + * @see #getClassPrefix() + */ + private final String classPrefix; + + /** + * Space of class document. + * + * @see #getClassSpace() + */ + private final String classSpace; + + /** + * Name of class document. + * + * @see #getClassName() + */ + private final String className; + + /** + * Full name of class document. + * + * @see #getClassFullName() + */ + private final String classFullName; + + private final ClassReference classReference; + + /** + * Space of class sheet document. + * + * @see #getClassSpace() + */ + private final String classSheetSpace; + + /** + * Name of class sheet document. + * + * @see #getClassSheetName() + */ + private final String classSheetName; + + /** + * Full name of class sheet document. + * + * @see #getClassSheetFullName() + */ + private final String classSheetFullName; + private DocumentReference classSheetDocRef; + + /** + * Space of class template document. + * + * @see #getClassSpace() + */ + private final String classTemplateSpace; + + /** + * Name of class template document. + * + * @see #getClassTemplateName() + */ + private final String classTemplateName; + + /** + * Full name of class template document. + * + * @see #getClassTemplateDocRef() + */ + private final DocumentReference classTemplateDocRef; + + /** + * Default content of class template document. + */ + private final String classSheetDefaultContent; + + /** + * Default content of class sheet document. + */ + private final String classTemplateDefaultContent; + + /** + * Base class managed. + */ + private BaseClass baseClass; + + /** + * Indicate class Manager is updating class document. + */ + private boolean checkingClass; + + /** + * Indicate class Manager is updating class sheet document. + */ + private boolean checkingClassSheet; + + /** + * Indicate class Manager is updating class template document. + */ + private boolean checkingClassTemplate; + private final IModelAccessFacade modelAccess; + private final ModelContext mContext; + private final ModelUtils modelUtils; + + /** + * Constructor for AbstractXClassManager. + * + * @param prefix + * the prefix of class document. + * @see #AbstractXClassManager(String, String) + * @see #AbstractXClassManager(String, String, boolean) + */ + protected AbstractXClassManager(String prefix, IModelAccessFacade modelAccess, + ModelContext mContext, ModelUtils modelUtils) { + this(XWIKI_CLASS_SPACE_PREFIX, prefix, modelAccess, mContext, modelUtils); + } + + /** + * Constructor for AbstractXClassManager. + * + * @param spaceprefix + * the space prefix of class document. + * @param prefix + * the prefix of class document. + * @see #AbstractXClassManager(String) + * @see #AbstractXClassManager(String, String, boolean) + */ + protected AbstractXClassManager(String spaceprefix, String prefix, + IModelAccessFacade modelAccess, ModelContext mContext, ModelUtils modelUtils) { + this(spaceprefix, prefix, true, modelAccess, mContext, modelUtils); + } + + /** + * Constructor for AbstractXClassManager. + * + * @param spaceprefix + * the space of class document. + * @param prefix + * the prefix of class document. + * @param dispatch + * Indicate if it had to use standard XWiki applications space names. + * @see #AbstractXClassManager(String) + * @see #AbstractXClassManager(String, String) + */ + protected AbstractXClassManager(String spaceprefix, String prefix, boolean dispatch, + IModelAccessFacade modelAccess, ModelContext mContext, ModelUtils modelUtils) { + this.modelAccess = modelAccess; + this.mContext = mContext; + this.modelUtils = modelUtils; + this.classSpacePrefix = spaceprefix; + this.classPrefix = prefix; + + this.classSpace = dispatch ? classSpacePrefix + XWIKI_CLASS_SPACE_SUFFIX : classSpacePrefix; + this.className = classPrefix + XWIKI_CLASS_SUFFIX; + this.classFullName = classSpace + XObjectDocument.SPACE_DOC_SEPARATOR + className; + this.classReference = RefBuilder.create().space(classSpace).doc(className) + .build(ClassReference.class); + + this.classSheetSpace = dispatch ? classSpacePrefix + XWIKI_CLASSSHEET_SPACE_SUFFIX + : classSpacePrefix; + this.classSheetName = classPrefix + XWIKI_CLASSSHEET_SUFFIX; + this.classSheetFullName = classSheetSpace + XObjectDocument.SPACE_DOC_SEPARATOR + + classSheetName; + this.classSheetDocRef = RefBuilder.from(mContext.getWikiRef()).space(classSheetSpace) + .doc(classSheetName).build(DocumentReference.class); + + this.classTemplateSpace = dispatch ? classSpacePrefix + XWIKI_CLASSTEMPLATE_SPACE_SUFFIX + : classSpacePrefix; + this.classTemplateName = classPrefix + XWIKI_CLASSTEMPLATE_SUFFIX; + this.classTemplateDocRef = RefBuilder.from(mContext.getWikiRef()).space(classTemplateSpace) + .doc(classTemplateName).build(DocumentReference.class); + + this.classSheetDefaultContent = "## you can modify this page to customize the presentation of your object\n\n" + + "1 Document $doc.name\n\n#set($class = $doc.getObject(\"" + classFullName + + "\").xWikiClass)\n" + + "\n" + "
\n" + " #foreach($prop in $class.properties)\n" + + "
${prop.prettyName}
\n" + + "
$doc.display($prop.getName())
\n #end\n" + "
\n"; + + this.classTemplateDefaultContent = "#includeForm(\"" + classSheetFullName + "\")\n"; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#getClassSpacePrefix() + */ + @Override + public String getClassSpacePrefix() { + return this.classSpacePrefix; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassSpace() + */ + @Override + public String getClassSpace() { + return this.classSpace; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassPrefix() + */ + @Override + public String getClassPrefix() { + return this.classPrefix; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassName() + */ + @Override + public String getClassName() { + return this.className; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassFullName() + */ + @Deprecated + @Override + public String getClassFullName() { + return this.classFullName; + } + + @Override + public DocumentReference getClassDocRef() { + return this.classReference.getDocRef(); + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassTemplateName() + */ + @Override + public String getClassTemplateSpace() { + return this.classTemplateSpace; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassTemplateName() + */ + @Override + public String getClassTemplateName() { + return this.classTemplateName; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassTemplateFullName() + */ + @Deprecated + @Override + public String getClassTemplateFullName() { + return modelUtils.serializeRef(this.classTemplateDocRef, ReferenceSerializationMode.LOCAL); + } + + @Override + public DocumentReference getClassTemplateDocRef() { + return this.classTemplateDocRef; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassSheetName() + */ + @Override + public String getClassSheetSpace() { + return this.classSheetSpace; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassSheetName() + */ + @Override + public String getClassSheetName() { + return this.classSheetName; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassSheetFullName() + */ + @Deprecated + @Override + public String getClassSheetFullName() { + return this.classSheetFullName; + } + + @Override + public DocumentReference getClassSheetDocRef() { + return this.classSheetDocRef; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#forceValidDocumentName() + */ + @Override + public boolean forceValidDocumentName() { + return false; + } + + /** + * Check if all necessary documents for manage this class in this context exists and update. + * Create if not exists. + * Thread safe. + * + * @param context + * the XWiki context. + * @throws XWikiException + * error when saving documents. + * @see #checkClassDocument(XWikiContext) + */ + protected void check(XWikiContext context) throws XWikiException { + checkClassDocument(context); + checkClassSheetDocument(context); + checkClassTemplateDocument(context); + } + + /** + * Check if class document exists in this context and update. Create if not exists. + * + * @param context + * the XWiki context. + * @throws XWikiException + * error when saving document. + */ + private void checkClassDocument(XWikiContext context) throws XWikiException { + if (this.checkingClass) { + return; + } + + this.checkingClass = true; + + try { + XWikiDocument doc = modelAccess.getOrCreateDocument(getClassDocRef()); + doc.setParent(DEFAULT_XWIKICLASS_PARENT); + boolean needsUpdate = doc.isNew(); + + this.baseClass = doc.getxWikiClass(); + + needsUpdate |= updateBaseClass(this.baseClass); + + if (doc.isNew() || needsUpdate) { + modelAccess.saveDocument(doc); + } + } catch (DocumentSaveException exp) { + throw new XWikiException(XWikiException.MODULE_XWIKI_PLUGINS, + XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_DOC, exp.getMessage(), exp); + } finally { + this.checkingClass = false; + } + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassSheetDefaultContent() + */ + @Override + public String getClassSheetDefaultContent() { + return this.classSheetDefaultContent; + } + + /** + * Load an entire resource text file into {@link String}. + * + * @param path + * the path to the resource file. + * @return the entire content of the resource text file. + */ + private String getResourceDocumentContent(String path) { + InputStream in = this.getClass().getClassLoader().getResourceAsStream(path); + + if (in != null) { + try { + StringBuffer content = new StringBuffer(in.available()); + + InputStreamReader isr = new InputStreamReader(in); + try (isr) { + BufferedReader reader = new BufferedReader(isr); + for (String str = reader.readLine(); str != null; str = reader.readLine()) { + content.append(str); + content.append('\n'); + } + } + + return content.toString(); + } catch (IOException e) { + // No resource file as been found or there is a problem when read it. + } + } + + return null; + } + + /** + * Check if class sheet document exists in this context and update. Create if not exists. + * + * @param context + * the XWiki context. + * @throws XWikiException + * error when saving document. + */ + private void checkClassSheetDocument(XWikiContext context) throws XWikiException { + if (this.checkingClassSheet) { + return; + } + + this.checkingClassSheet = true; + + try { + XWikiDocument doc = modelAccess.getOrCreateDocument(getClassSheetDocRef()); + doc.setParent(getClassFullName()); + boolean needsUpdate = doc.isNew(); + + if (doc.isNew()) { + String documentContentPath = DOCUMENTCONTENT_SHEET_PREFIX + getClassSheetFullName() + + DOCUMENTCONTENT_EXT; + String content = getResourceDocumentContent(documentContentPath); + doc.setContent(content != null ? content : getClassSheetDefaultContent()); + doc.setSyntax(Syntax.XWIKI_1_0); + } + + if (doc.isNew() || needsUpdate) { + modelAccess.saveDocument(doc); + } + } catch (DocumentSaveException exp) { + throw new XWikiException(XWikiException.MODULE_XWIKI_PLUGINS, + XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_DOC, exp.getMessage(), exp); + } finally { + this.checkingClassSheet = false; + } + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassTemplateDefaultContent() + */ + @Override + public String getClassTemplateDefaultContent() { + return this.classTemplateDefaultContent; + } + + /** + * Check if class template document exists in this context and update. Create if not exists. + * + * @param context + * the XWiki context. + * @throws XWikiException + * error when saving document. + */ + private void checkClassTemplateDocument(XWikiContext context) throws XWikiException { + if (this.checkingClassTemplate) { + return; + } + + this.checkingClassTemplate = true; + + try { + XWikiDocument doc = modelAccess.getOrCreateDocument(getClassTemplateDocRef()); + boolean needsUpdate = doc.isNew(); + + if (doc.getObject(getClassFullName()) == null) { + doc.createNewObject(getClassFullName(), context); + + needsUpdate = true; + } + + if (doc.isNew()) { + String content = getResourceDocumentContent( + DOCUMENTCONTENT_TEMPLATE_PREFIX + getClassTemplateFullName() + + DOCUMENTCONTENT_EXT); + doc.setContent(content != null ? content : getClassTemplateDefaultContent()); + doc.setSyntax(Syntax.XWIKI_1_0); + + doc.setParent(getClassFullName()); + } + + needsUpdate |= updateClassTemplateDocument(doc); + + if (doc.isNew() || needsUpdate) { + modelAccess.saveDocument(doc); + } + } catch (DocumentSaveException exp) { + throw new XWikiException(XWikiException.MODULE_XWIKI_PLUGINS, + XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_DOC, exp.getMessage(), exp); + } finally { + this.checkingClassTemplate = false; + } + } + + /** + * @param value + * the {@link Boolean} value to convert. + * @return the converted int value. + */ + protected int intFromBoolean(Boolean value) { + return value == null ? -1 : (value ? 1 : 0); + } + + /** + * Initialize template document with default content. + * + * @param doc + * the class template document that will be saved. + * @return true if doc modified. + */ + protected boolean updateClassTemplateDocument(XWikiDocument doc) { + return false; + } + + /** + * Set the value of a boolean field in a document. + * + * @param doc + * the document to modify. + * @param fieldName + * the name of the field. + * @param value + * the value. + * @return true if doc modified. + */ + protected boolean updateDocStringValue(XWikiDocument doc, String fieldName, String value) { + boolean needsUpdate = false; + + if (!value.equals(doc.getStringValue(getClassFullName(), fieldName))) { + doc.setStringValue(getClassFullName(), fieldName, value); + needsUpdate = true; + } + + return needsUpdate; + } + + /** + * Set the value of a boolean field in a document. + * + * @param doc + * the document to modify. + * @param fieldName + * the name of the field. + * @param value + * the value. + * @return true if doc modified. + */ + protected boolean updateDocBooleanValue(XWikiDocument doc, String fieldName, Boolean value) { + boolean needsUpdate = false; + + int intvalue = intFromBoolean(value); + + if (intvalue != doc.getIntValue(getClassFullName(), fieldName)) { + doc.setIntValue(getClassFullName(), fieldName, intvalue); + needsUpdate = true; + } + + return needsUpdate; + } + + /** + * Configure BaseClass. + * + * @param baseClass + * the baseClass to configure. + * @return true if baseClass modified. + */ + protected boolean updateBaseClass(BaseClass baseClass) { + boolean needUpdate = false; + + if (!baseClass.getName().equals(getClassFullName())) { + baseClass.setName(getClassFullName()); + needUpdate = true; + } + + return needUpdate; + } + + /** + * Set the default value of a boolean field of a XWiki class. + * + * @param baseClass + * the XWiki class. + * @param fieldName + * the name of the field. + * @param value + * the default value. + * @return true if baseClass modified. + */ + protected boolean updateBooleanClassDefaultValue(BaseClass baseClass, String fieldName, + Boolean value) { + boolean needsUpdate = false; + + BooleanClass bc = (BooleanClass) baseClass.get(fieldName); + + int old = bc.getDefaultValue(); + int intvalue = intFromBoolean(value); + + if (intvalue != old) { + bc.setDefaultValue(intvalue); + needsUpdate = true; + } + + return needsUpdate; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getBaseClass() + */ + @Override + public BaseClass getBaseClass() { + if (this.baseClass == null) { + this.baseClass = new BaseClass(); + updateBaseClass(this.baseClass); + } + + return this.baseClass; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassDocument(com.xpn.xwiki.XWikiContext) + */ + @Override + public Document getClassDocument(XWikiContext context) throws XWikiException { + check(context); + + return modelAccess.getOrCreateDocument(getClassDocRef()).newDocument(context); + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassSheetDocument(com.xpn.xwiki.XWikiContext) + */ + @Override + public Document getClassSheetDocument(XWikiContext context) throws XWikiException { + check(context); + + return modelAccess.getOrCreateDocument(getClassSheetDocRef()).newDocument(context); + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getClassTemplateDocument(com.xpn.xwiki.XWikiContext) + */ + @Override + public Document getClassTemplateDocument(XWikiContext context) throws XWikiException { + check(context); + + return modelAccess.getOrCreateDocument(getClassTemplateDocRef()).newDocument(context); + } + + /** + * {@inheritDoc} + * + * @see XClassManager#isInstance(com.xpn.xwiki.doc.XWikiDocument) + */ + @Override + public boolean isInstance(XWikiDocument doc) { + return (doc.getObjectNumbers(getClassFullName()) > 0) + && (!forceValidDocumentName() || isValidName(doc.getFullName())); + } + + /** + * {@inheritDoc} + * + * @see XClassManager#isInstance(com.xpn.xwiki.doc.XWikiDocument) + */ + @Override + public boolean isInstance(Document doc) { + return (doc.getObjectNumbers(getClassFullName()) > 0) + && (!forceValidDocumentName() || isValidName(doc.getFullName())); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#isValidName(java.lang.String) + */ + @Override + public boolean isValidName(String fullName) { + return getItemDefaultName(fullName) != null; + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getItemDocumentDefaultName(java.lang.String, XWikiContext) + */ + @Override + public String getItemDocumentDefaultName(String itemName, XWikiContext context) { + String cleanedItemName = context != null + ? context.getWiki().clearName(itemName, true, true, context) + : itemName; + + return getClassPrefix() + cleanedItemName.substring(0, 1).toUpperCase() + + cleanedItemName.substring(1).toLowerCase(); + } + + /** + * {@inheritDoc} + * + * @see XClassManager#getItemDocumentDefaultFullName(java.lang.String, XWikiContext) + */ + @Override + public String getItemDocumentDefaultFullName(String itemName, XWikiContext context) { + return getClassSpacePrefix() + XObjectDocument.SPACE_DOC_SEPARATOR + + getItemDocumentDefaultName(itemName, context); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#getItemDefaultName(java.lang.String) + */ + @Override + public String getItemDefaultName(String docFullName) { + String prefix = getClassSpacePrefix() + XObjectDocument.SPACE_DOC_SEPARATOR + getClassPrefix(); + + if (!docFullName.startsWith(prefix)) { + return null; + } + + return docFullName.substring(prefix.length()).toLowerCase(); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#getXObjectDocument(java.lang.String, + * int, boolean, com.xpn.xwiki.XWikiContext) + */ + @Override + public T getXObjectDocument(String itemName, int objectId, boolean validate, XWikiContext context) + throws XWikiException { + XWikiDocument doc = context.getWiki() + .getDocument(getItemDocumentDefaultFullName(itemName, context), context); + + if (doc.isNew() || !isInstance(doc)) { + throw new XObjectDocumentDoesNotExistException(itemName + " object does not exist"); + } + + return newXObjectDocument(doc, objectId, context); + } + + /** + * Construct HQL where clause to use with {@link com.xpn.xwiki.store.XWikiStoreInterface} + * "searchDocuments" methods. + * + * @param fieldDescriptors + * the list of fields name/value constraints. Format : [[fieldName1, typeField1, + * valueField1][fieldName2, typeField2, valueField2]]. + * @param parameterValues + * the where clause values that replace the question marks (?). + * @return a HQL where clause. + */ + @Override + public String createWhereClause(Object[][] fieldDescriptors, List parameterValues) { + StringBuffer from = new StringBuffer(", BaseObject as obj"); + + StringBuffer where = new StringBuffer( + " where doc.fullName=obj.name and obj.className=" + HQL_PARAMETER_STRING); + parameterValues.add(getClassFullName()); + + if (forceValidDocumentName()) { + where.append(" and doc.fullName LIKE" + HQL_PARAMETER_STRING); + parameterValues.add(getItemDocumentDefaultFullName("%", null)); + } + + where.append(" and doc.fullName<>" + HQL_PARAMETER_STRING); + parameterValues.add(getClassTemplateFullName()); + + String andSymbol = " and "; + + if (fieldDescriptors != null) { + for (int i = 0; i < fieldDescriptors.length; ++i) { + String fieldName = (String) fieldDescriptors[i][0]; + String type = (String) fieldDescriptors[i][1]; + Object value = fieldDescriptors[i][2]; + + if (type != null) { + String fieldPrefix = "field" + i; + + from.append(", " + type + " as " + fieldPrefix); + + where.append(andSymbol + "obj.id=" + fieldPrefix + ".id.id"); + + where.append(andSymbol + fieldPrefix + ".name=" + HQL_PARAMETER_STRING); + parameterValues.add(fieldName); + + if (value instanceof String) { + where.append(andSymbol + "lower(" + fieldPrefix + ".value)=" + HQL_PARAMETER_STRING); + parameterValues.add(((String) value).toLowerCase()); + } else { + where.append(andSymbol + "" + fieldPrefix + ".value=" + HQL_PARAMETER_STRING); + parameterValues.add(value); + } + } else { + if (value instanceof String) { + where.append(" and lower(doc." + fieldName + ")=" + HQL_PARAMETER_STRING); + parameterValues.add(((String) value).toLowerCase()); + } else { + where.append(" and doc." + fieldName + "=" + HQL_PARAMETER_STRING); + parameterValues.add(value); + } + } + } + } + + return from.append(where).toString(); + } + + /** + * Find all XWikiDocument containing object of this XWiki class. + * + * @param context + * the XWiki context. + * @return the list of found {@link XObjectDocument}. + * @throws XWikiException + * error when searching for document in database. + * @see #getClassFullName() + */ + public List searchXObjectDocuments(XWikiContext context) throws XWikiException { + return searchXObjectDocumentsByFields(null, context); + } + + /** + * Search in instances of this document class. + * + * @param fieldName + * the name of field. + * @param fieldValue + * the value of field. + * @param fieldType + * the type of field. + * @param context + * the XWiki context. + * @return the list of found {@link T}. + * @throws XWikiException + * error when searching for documents from in database. + */ + public List searchXObjectDocumentsByField(String fieldName, Object fieldValue, + String fieldType, + XWikiContext context) throws XWikiException { + Object[][] fieldDescriptors = new Object[][] { { fieldName, fieldType, fieldValue } }; + + return searchXObjectDocumentsByFields(fieldDescriptors, context); + } + + /** + * Search in instances of this document class. + * + * @param fieldDescriptors + * the list of fields name/value constraints. Format : [[fieldName1, typeField1, + * valueField1][fieldName2, typeField2, valueField2]]. + * @param context + * the XWiki context. + * @return the list of found {@link XObjectDocument}. + * @throws XWikiException + * error when searching for documents from in database. + */ + public List searchXObjectDocumentsByFields(Object[][] fieldDescriptors, XWikiContext context) + throws XWikiException { + List parameterValues = new ArrayList<>(); + String where = createWhereClause(fieldDescriptors, parameterValues); + + return newXObjectDocumentList( + context.getWiki().getStore().searchDocuments(where, parameterValues, context), + context); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#newXObjectDocument(com.xpn.xwiki.doc.XWikiDocument, + * int, com.xpn.xwiki.XWikiContext) + */ + @Override + public T newXObjectDocument(XWikiDocument doc, int objId, XWikiContext context) + throws XWikiException { + return (T) new DefaultXObjectDocument(this, doc, objId, context); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#newXObjectDocument(java.lang.String, + * int, com.xpn.xwiki.XWikiContext) + */ + @Override + public T newXObjectDocument(DocumentReference docDocRef, int objId, XWikiContext context) + throws XWikiException { + return newXObjectDocument(modelAccess.getOrCreateDocument(docDocRef), objId, context); + } + + /** + * {@inheritDoc} + * + * @see XClassManager#newXObjectDocument(com.xpn.xwiki.XWikiContext) + */ + @Override + public T newXObjectDocument(XWikiContext context) throws XWikiException { + return newXObjectDocument(new XWikiDocument(), 0, context); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#newXObjectDocumentList(com.xpn.xwiki.doc.XWikiDocument, + * com.xpn.xwiki.XWikiContext) + */ + @Override + public List newXObjectDocumentList(XWikiDocument document, XWikiContext context) + throws XWikiException { + List documents = new ArrayList<>(1); + documents.add(document); + + return newXObjectDocumentList(documents, context); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager#newXObjectDocumentList(java.util.List, + * com.xpn.xwiki.XWikiContext) + */ + @Override + public List newXObjectDocumentList(List documents, XWikiContext context) + throws XWikiException { + List list; + + if (!documents.isEmpty()) { + check(context); + + list = new ArrayList<>(documents.size()); + + for (XWikiDocument doc : documents) { + List objects = doc.getObjects(getClassFullName()); + + for (BaseObject bobject : objects) { + if (bobject != null) { + list.add(newXObjectDocument(doc, bobject.getNumber(), context)); + } + } + } + } else { + list = Collections.emptyList(); + } + + return list; + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/DefaultXObjectDocument.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/DefaultXObjectDocument.java new file mode 100644 index 000000000..95b3eb5cf --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/DefaultXObjectDocument.java @@ -0,0 +1,508 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes; + +import java.util.List; + +import com.celements.model.access.IModelAccessFacade; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.api.Document; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.objects.BaseElement; +import com.xpn.xwiki.objects.BaseObject; +import com.xpn.xwiki.objects.BaseProperty; +import com.xpn.xwiki.objects.PropertyInterface; +import com.xpn.xwiki.objects.classes.PropertyClass; +import com.xpn.xwiki.web.Utils; + +/** + * Default implementation of XObjectDocument. This class manage an XWiki document containing + * provided XWiki class. It + * add some specifics methods, getters and setters for this type of object and fields. It also + * override {@link Document} + * (and then {@link XWikiDocument}) isNew concept considering as new a document that does not + * contains an XWiki object + * of the provided XWiki class. + * + * @version $Id$ + * @see XObjectDocument + * @see XClassManager + * @since Application Manager 1.0RC1 + */ +public class DefaultXObjectDocument extends Document implements XObjectDocument { + + /** + * Value in int for {@link Boolean#TRUE}. + */ + private static final int BOOLEANFIELD_TRUE = 1; + + /** + * Value in int for {@link Boolean#FALSE}. + */ + private static final int BOOLEANFIELD_FALSE = 0; + + /** + * Value in int for {@link Boolean} = null. + */ + private static final int BOOLEANFIELD_MAYBE = 2; + + /** + * The class manager for this document. + */ + protected XClassManager sclass; + + /** + * The id of the XWiki object included in the document to manage. + */ + protected int objectId; + + /** + * true if this is a new document of this class (this document can exist but does not contains + * object of this + * class). + */ + protected boolean isNew; + + private final IModelAccessFacade modelAccess; + + /** + * Create instance of DefaultXObjectDocument from provided XWikiDocument. + * + * @param sclass + * the class manager for this document. + * @param xdoc + * the XWikiDocument to manage. + * @param objectId + * the id of the XWiki object included in the document to manage. + * @param context + * the XWiki context. + * @throws XWikiException + * error when calling {@link #reload(XWikiContext)}. + */ + public DefaultXObjectDocument(XClassManager sclass, XWikiDocument xdoc, + int objectId, XWikiContext context) throws XWikiException { + super(xdoc, context); + this.modelAccess = Utils.getComponent(IModelAccessFacade.class); + + this.sclass = sclass; + this.objectId = objectId; + + reload(context); + } + + /** + * @param docFullName + * modify the full name the the managed XWikiDocument. + */ + public void setFullName(String docFullName) { + getDoc().setFullName(docFullName, context); + } + + /** + * {@inheritDoc} + * + * @see XObjectDocument#reload(com.xpn.xwiki.XWikiContext) + */ + @Override + public void reload(XWikiContext context) throws XWikiException { + if (this.getObjectNumbers(this.sclass.getClassFullName()) == 0) { + if (this.objectId > 0) { + throw new XWikiException(XWikiException.MODULE_XWIKI_DOC, + XWikiException.ERROR_XWIKI_DOES_NOT_EXIST, + "Object od id " + this.objectId + "does not exist"); + } + + BaseObject object = getDoc().newXObject(this.sclass.getClassDocRef(), context); + + XWikiDocument docTemplate = modelAccess + .getOrCreateDocument(this.sclass.getClassTemplateDocRef()); + BaseObject templateObject = docTemplate.getXObject(this.sclass.getClassDocRef()); + + if (templateObject != null) { + object.merge(templateObject); + } + + if (super.isNew()) { + setParent(docTemplate.getParent()); + setContent(docTemplate.getContent()); + setSyntaxId(docTemplate.getSyntaxId()); + } + + this.isNew = true; + } + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XObjectDocument#getDocumentApi() + */ + @Override + public Document getDocumentApi() { + return this; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XObjectDocument#getObjectId() + */ + @Override + public int getObjectId() { + return this.objectId; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XObjectDocument#getObjectApi() + */ + @Override + public com.xpn.xwiki.api.Object getObjectApi() { + BaseObject obj = getBaseObject(false); + + return obj == null ? null : obj.newObjectApi(obj, context); + } + + /** + * Get the managed {@link BaseObject}. + * + * @param toWrite + * indicate that the {@link BaseObject} will be modified. + * @return the {@link BaseObject}. + */ + protected BaseObject getBaseObject(boolean toWrite) { + BaseObject obj; + + if (toWrite) { + obj = getDoc().getXObject(this.sclass.getClassDocRef(), this.objectId); + } else { + obj = this.doc.getXObject(this.sclass.getClassDocRef(), this.objectId); + } + + return obj; + } + + /** + * Overwrite current BaseObject fields with provided one. Only provided non null fields are + * copied. + * + * @param sdoc + * the document to merge. + */ + public void mergeObject(DefaultXObjectDocument sdoc) { + if (getXClassManager() != sdoc.getXClassManager()) { + return; + } + + BaseObject obj1 = getBaseObject(true); + BaseObject obj2 = sdoc.getBaseObject(false); + + for (String fieldName : obj2.getPropertyList()) { + PropertyInterface fieldValue2 = obj2.safeget(fieldName); + if (fieldValue2 != null) { + obj1.safeput(fieldName, (PropertyInterface) ((BaseElement) fieldValue2).clone()); + } + } + } + + /** + * {@inheritDoc} + * + * @see XObjectDocument#getXClassManager() + */ + @Override + public XClassManager getXClassManager() { + return this.sclass; + } + + /** + * {@inheritDoc} + * + * @see XObjectDocument#isNew() + */ + @Override + public boolean isNew() { + return super.isNew() || this.isNew; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.api.Document#saveDocument(java.lang.String, boolean) + */ + @Override + protected void saveDocument(String comment, boolean minorEdit) throws XWikiException { + super.saveDocument(comment, minorEdit); + this.isNew = false; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.api.Document#delete() + */ + @Override + public void delete() throws XWikiException { + if (getObjectNumbers(sclass.getClassFullName()) == 1) { + super.delete(); + } else { + doc.removeObject(getBaseObject(false)); + save(); + } + + this.isNew = true; + } + + /** + * Get the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @return the value in {@link String} of the field fieldName of the managed object's + * class. + * @see com.xpn.xwiki.doc.XWikiDocument#getStringValue(java.lang.String) + */ + public String getStringValue(String fieldName) { + BaseObject obj = getBaseObject(false); + + if (obj == null) { + return null; + } + + return obj.getStringValue(fieldName); + } + + /** + * Modify the value of the field fieldName of the managed object's class. + *

+ * This method makes sure the right property type between LargeStringProperty and StringProperty + * is used. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @param value + * the new value of the field fieldName of the managed object's class. + * @see com.xpn.xwiki.doc.XWikiDocument#setStringValue(java.lang.String,java.lang.String,java.lang.String) + */ + public void setStringValue(String fieldName, String value) { + BaseObject obj = getBaseObject(true); + + if (obj != null) { + PropertyClass pclass = (PropertyClass) this.sclass.getBaseClass().get(fieldName); + + if (pclass != null) { + BaseProperty prop = (BaseProperty) obj.safeget(fieldName); + prop = pclass.fromString(value); + if (prop != null) { + obj.safeput(fieldName, prop); + } + } + } + } + + /** + * Get the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @return the value in {@link String} of the field fieldName of the managed object's + * class. + * @see com.xpn.xwiki.doc.XWikiDocument#getStringValue(java.lang.String) + * @deprecated Use {@link #getStringValue(String)} which support LargeStringProperty and + * StringProperty. + */ + @Deprecated + public String getLargeStringValue(String fieldName) { + BaseObject obj = getBaseObject(false); + + if (obj == null) { + return null; + } + + return obj.getLargeStringValue(fieldName); + } + + /** + * Modify the value of the field fieldName of the managed object's class. + *

+ * This method makes sure the right property type between LargeStringProperty and StringProperty + * is used. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @param value + * the new value of the field fieldName of the managed object's class. + * @see com.xpn.xwiki.doc.XWikiDocument#setLargeStringValue(java.lang.String,java.lang.String,java.lang.String) + * @deprecated Use {@link #setStringValue(String, String)} which support LargeStringProperty and + * StringProperty. + */ + @Deprecated + public void setLargeStringValue(String fieldName, String value) { + setStringValue(fieldName, value); + } + + /** + * Get the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @return the value in {@link List} of the field fieldName of the managed object's + * class. + * @see com.xpn.xwiki.doc.XWikiDocument#getListValue(java.lang.String) + * @since 1.4 + */ + public List getStringListValue(String fieldName) { + BaseObject obj = getBaseObject(false); + + if (obj == null) { + return null; + } + + return obj.getListValue(fieldName); + } + + /** + * Modify the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @param value + * the new value of the field fieldName of the managed object's class. + * @see com.xpn.xwiki.doc.XWikiDocument#setStringListValue(java.lang.String,java.lang.String,java.util.List) + * @since 1.4 + */ + public void setStringListValue(String fieldName, List value) { + BaseObject obj = getBaseObject(true); + + if (obj != null) { + obj.setStringListValue(fieldName, value); + } + } + + /** + * Get the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @return the value in {@link List} of the field fieldName of the managed object's + * class. + * @see com.xpn.xwiki.doc.XWikiDocument#getListValue(java.lang.String) + * @deprecated Use {@link #getStringListValue(String)} instead. Since 1.4. + */ + @Deprecated + public List getListValue(String fieldName) { + BaseObject obj = getBaseObject(false); + + if (obj == null) { + return null; + } + + return obj.getListValue(fieldName); + } + + /** + * Modify the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @param value + * the new value of the field fieldName of the managed object's class. + * @see com.xpn.xwiki.doc.XWikiDocument#setStringListValue(java.lang.String,java.lang.String,java.util.List) + * @deprecated Use {@link #getStringListValue(String)} instead. Since 1.4. + */ + @Deprecated + public void setListValue(String fieldName, List value) { + BaseObject obj = getBaseObject(true); + + if (obj != null) { + obj.setStringListValue(fieldName, value); + } + } + + /** + * Get the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @return the value in int of the field fieldName of the managed object's class. + * @see com.xpn.xwiki.doc.XWikiDocument#getListValue(java.lang.String) + */ + public int getIntValue(String fieldName) { + BaseObject obj = getBaseObject(false); + + if (obj == null) { + return 0; + } + + return obj.getIntValue(fieldName); + } + + /** + * Modify the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @param value + * the new value of the field fieldName of the managed object's class. + * @see com.xpn.xwiki.doc.XWikiDocument#setIntValue(String, String, int) + */ + public void setIntValue(String fieldName, int value) { + BaseObject obj = getBaseObject(true); + + if (obj != null) { + obj.setIntValue(fieldName, value); + } + } + + /** + * Get the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @return the value in {@link Boolean} of the field fieldName of the managed + * object's class. + * @see com.xpn.xwiki.doc.XWikiDocument#getListValue(java.lang.String) + */ + public Boolean getBooleanValue(String fieldName) { + int intValue = getIntValue(fieldName); + + return intValue == BOOLEANFIELD_TRUE ? Boolean.TRUE + : (intValue == BOOLEANFIELD_FALSE ? Boolean.FALSE : null); + } + + /** + * Modify the value of the field fieldName of the managed object's class. + * + * @param fieldName + * the name of the field from the managed object's class where to find the value. + * @param value + * the new value of the field fieldName of the managed object's class. + * @see com.xpn.xwiki.doc.XWikiDocument#setIntValue(String, String, int) + */ + public void setBooleanValue(String fieldName, Boolean value) { + setIntValue(fieldName, + value == null ? BOOLEANFIELD_MAYBE + : (value ? BOOLEANFIELD_TRUE + : BOOLEANFIELD_FALSE)); + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XClassManager.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XClassManager.java new file mode 100644 index 000000000..bee27b76d --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XClassManager.java @@ -0,0 +1,455 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes; + +import java.util.List; + +import org.xwiki.model.reference.DocumentReference; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.api.Document; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.objects.classes.BaseClass; + +/** + * Interface that give way to manage a XWiki class. + *

    + *
  • assume that the XWiki class exist in the context we are working + *
  • search in documents that contains this class with conditions on class fields + *
  • support the XWiki norm about spaces and documents naming + *
+ * + * @param + * the item class extending {@link XObjectDocument}. + * @version $Id$ + * @since Application Manager 1.0RC1 + */ +public interface XClassManager { + + /** + * Default suffix for the document containing the class. + */ + String XWIKI_CLASS_SUFFIX = "Class"; + + /** + * Default suffix for a document containing a class sheet. + */ + String XWIKI_CLASSSHEET_SUFFIX = "ClassSheet"; + + /** + * Default suffix for a document containing a class template. + */ + String XWIKI_CLASSTEMPLATE_SUFFIX = "ClassTemplate"; + + /** + * Default prefix for a document's space containing a class. + */ + String XWIKI_CLASS_SPACE_PREFIX = "XWiki"; + + /** + * Default suffix for a document's space containing a class. + */ + String XWIKI_CLASS_SPACE_SUFFIX = "Classes"; + + /** + * Default suffix for a document's space containing a class sheet. + */ + String XWIKI_CLASSSHEET_SPACE_SUFFIX = "Sheets"; + + /** + * Default suffix for a document's space containing a class template. + */ + String XWIKI_CLASSTEMPLATE_SPACE_SUFFIX = "Templates"; + + // /// + + /** + * @return the space prefix name of the document containing the class. + */ + String getClassSpacePrefix(); + + /** + * @return the prefix name of the document containing the class. Usually extracted from : + * ClassSpace.ClassPrefixXWIKI_CLASS_SUFFIX. + * @see #getClassSpace() + */ + String getClassPrefix(); + + // Classes + + /** + * @return the space name of the document containing the class. Usually class space is : + * SpacePrefixXWIKI_CLASS_SPACE_SUFFIX. + * @see #getClassSpacePrefix() + * @see #XWIKI_CLASS_SPACE_SUFFIX + */ + String getClassSpace(); + + /** + * @return the name of the document containing the class. Usually class name is : + * ClassPrefixXWIKI_CLASS_SUFFIX. + * @see #getClassFullName() + * @see #getClassPrefix() + * @see #XWIKI_CLASS_SUFFIX + */ + String getClassName(); + + /** + * @return the full name of the document containing the class. Usually class full name is : + * ClassSpace.ClassName. + * @see #getClassName() + * @see #getClassSpace() + * @deprecated since 6.0 instead use getClassDocRef + */ + @Deprecated + String getClassFullName(); + + /** + * @return the document reference of the document containing the class. Usually class document + * reference is: ClassSpace.ClassName. + * @see #getClassName() + * @see #getClassSpace() + */ + DocumentReference getClassDocRef(); + + // Templates + + /** + * @return the space name of the document containing the class template. Usually class template + * space name is : + * ClassSpacePrefixXWIKI_CLASSTEMPLATE_SPACE_SUFFIX. + * @see #getClassPrefix() + * @see #XWIKI_CLASSTEMPLATE_SPACE_SUFFIX + */ + String getClassTemplateSpace(); + + /** + * @return the name of the document containing the class template. Usually class template name is: + * ClassPrefixClassXWIKI_CLASSTEMPLATE_SUFFIX. + * @see #getClassPrefix() + * @see #getClassTemplateFullName() + * @see #XWIKI_CLASSTEMPLATE_SUFFIX + */ + String getClassTemplateName(); + + /** + * @return the full name of the document containing the class template. Usually class template + * full name is : ClassTemplateSpace.ClassTemplateName. + * @see #getClassTemplateSpace() + * @see #getClassTemplateName() + * @deprecated since 6.0 instead use getClassTemplateDocRef + */ + @Deprecated + String getClassTemplateFullName(); + + /** + * @return the document reference of the document containing the class template. Usually class + * template document reference is : ClassTemplateSpace.ClassTemplateName. + * @see #getClassTemplateSpace() + * @see #getClassTemplateName() + */ + DocumentReference getClassTemplateDocRef(); + + // Sheets + + /** + * @return the space name of the document containing the class sheet. Usually class sheet space + * name is : + * ClassSpacePrefixXWIKI_CLASSSHEET_SPACE_SUFFIX. + * @see #getClassSpacePrefix() + * @see #XWIKI_CLASSSHEET_SPACE_SUFFIX + */ + String getClassSheetSpace(); + + /** + * @return the name of the document containing the class sheet. Usually class sheet name is : + * ClassPrefixXWIKI_CLASSSHEET_SUFFIX. + * @see #getClassPrefix() + * @see #getClassSheetFullName() + * @see #XWIKI_CLASSSHEET_SUFFIX + */ + String getClassSheetName(); + + /** + * @return the full name of the document containing the class sheet. Usually class sheet full name + * is : + * ClassSheetSpace.ClassSheetName. + * @see #getClassSheetSpace() + * @see #getClassSheetName() + * @deprecated + */ + @Deprecated + String getClassSheetFullName(); + + /** + * @return the document reference of the document containing the class sheet. Usually class sheet + * full name is : + * ClassSheetSpace.ClassSheetName. + * @see #getClassSheetSpace() + * @see #getClassSheetName() + */ + public DocumentReference getClassSheetDocRef(); + + /** + * @return indicate if the document names has to respect the default generated name style. This is + * used in the + * search to limit to valid results. + */ + boolean forceValidDocumentName(); + + // /// + + /** + * @return the BaseClass managed by this SuperClass. + */ + BaseClass getBaseClass(); + + /** + * Get the document containing the class in this context's database. + * + * @param context + * the XWiki context. + * @return the document containing the class for this context. + * @throws XWikiException + * error when getting class document from the database. + */ + Document getClassDocument(XWikiContext context) throws XWikiException; + + /** + * @return the default content to add in a new class sheet document. + */ + String getClassSheetDefaultContent(); + + /** + * Get the document containing the class sheet for this context's database. + * + * @param context + * the XWiki context. + * @return the document containing the class sheet for this context. + * @throws XWikiException + * error when getting class sheet document from the database. + */ + Document getClassSheetDocument(XWikiContext context) throws XWikiException; + + /** + * @return the default content to add in a new class template document. + */ + String getClassTemplateDefaultContent(); + + /** + * Get the document containing the class template for this context's database. + * + * @param context + * the XWiki context. + * @return the class template document for this context. + * @throws XWikiException + * error when getting class template document from the database. + */ + Document getClassTemplateDocument(XWikiContext context) throws XWikiException; + + /** + * Determines if the specified doc is compatible with this xwiki class (if he + * contains class object). + * + * @param doc + * the XWikidocument to test. + * @return true if doc support this class, false otherwise. + */ + boolean isInstance(XWikiDocument doc); + + /** + * Determines if the specified doc is compatible with this xwiki class (if he + * contains class object). + * + * @param doc + * the XWikidocument to test. + * @return true if doc support this class, false otherwise. + */ + boolean isInstance(Document doc); + + /** + * @param fullName + * the full name of the document + * @return true if the document name follow the default rule + */ + boolean isValidName(String fullName); + + /** + * Get document name from item name item. Usually a Document name is + * DocumentTypeItemName. + * + * @param itemName + * the name of the item to find. + * @param context + * the XWiki context. + * @return the name of the document. + * @see #getItemDocumentDefaultFullName(String, XWikiContext) + * @see #getItemDefaultName(String) + */ + String getItemDocumentDefaultName(String itemName, XWikiContext context); + + /** + * Get document full name from item name item. Usually a Document full name is + * Space.DocumentTypeItemName. + * + * @param itemName + * the name of the item. + * @param context + * the XWiki context. + * @return the full name of the document. + * @see #getItemDocumentDefaultName(String, XWikiContext) + * @see #getItemDefaultName(String) + */ + String getItemDocumentDefaultFullName(String itemName, XWikiContext context); + + /** + * Get item name extracted from document full name. Usually a Document full name is + * Space.DocumentTypeItemName. + * + * @param docFullName + * the full name of the document. + * @return the item name extracted from document name. + * @see #getItemDocumentDefaultFullName(String, XWikiContext) + */ + String getItemDefaultName(String docFullName); + + /** + * Get document by full name from item name itemName. + * + * @param itemName + * the full name of the item. + * @param objectId + * the id of the XWiki object included in the document to manage. + * @param validate + * indicate if it return new {@link T} or throw exception if wiki descriptor does not + * exist. + * @param context + * the XWiki context. + * @return the document. + * @throws XWikiException + * error when getting document from the database. + * @see #getItemDefaultName(String) + * @see #getItemDocumentDefaultFullName(String, XWikiContext) + * @future XA2 : rename to getDocumentObject. + */ + T getXObjectDocument(String itemName, int objectId, boolean validate, XWikiContext context) + throws XWikiException; + + /** + * Construct HQL where clause to use with {@link com.xpn.xwiki.store.XWikiStoreInterface} + * "searchDocuments" methods. + * + * @param fieldDescriptors + * the list of fields name/value constraints. Format : [[fieldName1, typeField1, + * valueField1][fieldName2, typeField2, valueField2]]. + * @param parameterValues + * the where clause values filled by the method that replace the question marks (?). + * @return a HQL where clause. + */ + String createWhereClause(Object[][] fieldDescriptors, List parameterValues); + + /** + * Create new super document containing object of class {@link #getClassFullName()}. If document + * already exist it is + * returned with new object if it does not contains any. + * + * @param doc + * the XWiki document to manage. + * @param objId + * the id of the XWiki object included in the document to manage. + * @param context + * the XWiki context. + * @return a new XObjectDocument instance. + * @throws XWikiException + * error when calling XObjectDocument implementation constructor. + * @future XA2 : rename to newDocumentObject. + */ + T newXObjectDocument(XWikiDocument doc, int objId, XWikiContext context) throws XWikiException; + + /** + * Create new super document containing object of class {@link #getClassFullName()}. If document + * already exist it is + * returned with new object if it does not contains any. + * + * @param context + * the XWiki context. + * @return a new T instance. + * @throws XWikiException + * error when calling T constructor. + * @future XA2 : rename to newDocumentObject. + */ + T newXObjectDocument(XWikiContext context) throws XWikiException; + + /** + * Create new super document containing object of class {@link #getClassFullName()}. If document + * already exist it is + * returned with new object if it does not contains any. + * + * @param docFullName + * the full name of document to manage. + * @param objId + * the id of the XWiki object included in the document to manage. + * @param context + * the XWiki context. + * @return a new XObjectDocument instance. + * @throws XWikiException + * error when calling T constructor. + * @see #getClassFullName() + * @future XA2 : rename to newDocumentObject. + */ + T newXObjectDocument(DocumentReference docRef, int objId, XWikiContext context) + throws XWikiException; + + /** + * Create new super documents for each object of class {@link #getClassFullName()} for provided + * {@link XWikiDocument} and return it. + * + * @param documents + * the list of {@link XWikiDocument}. + * @param context + * the XWiki context. + * @return the list of {@link T}. + * @throws XWikiException + * error when calling XObjectDocument implementation constructor. + * @future XA2 : rename to newDocumentObjectList. + */ + List newXObjectDocumentList(XWikiDocument documents, XWikiContext context) + throws XWikiException; + + /** + * Create new super document for each object of class {@link #getClassFullName()} of each + * {@link XWikiDocument} in + * the list and return it. + * + * @param documents + * the list of {@link XWikiDocument}. + * @param context + * the XWiki context. + * @return the list of {@link T}. + * @throws XWikiException + * error when calling XObjectDocument implementation constructor. + * @future XA2 : rename to newDocumentObjectList. + */ + List newXObjectDocumentList(List documents, XWikiContext context) + throws XWikiException; + +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XObjectDocument.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XObjectDocument.java new file mode 100644 index 000000000..2de8b944f --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XObjectDocument.java @@ -0,0 +1,80 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.api.Document; +import com.xpn.xwiki.api.Object; + +/** + * SuperDocument interface. + * + * @version $Id$ + * @see XClassManager + * @since Application Manager 1.0RC1 + */ +public interface XObjectDocument +{ + /** + * The separator between space name and document name in document full name. + */ + String SPACE_DOC_SEPARATOR = "."; + + /** + * The separator between wiki name and space name and document extended full name. + */ + String WIKI_SPACE_SEPARATOR = ":"; + + /** + * Reload XWiki document from database using Document full name. + * + * @param context the XWiki context. + * @throws XWikiException error when initialize document. + */ + void reload(XWikiContext context) throws XWikiException; + + /** + * @return the class manager for this document. + */ + XClassManager< ? extends XObjectDocument> getXClassManager(); + + /** + * @return true if this is a new document of this class (this document can exist but does not contains object of + * this class). + */ + boolean isNew(); + + /** + * @return the document. + */ + Document getDocumentApi(); + + /** + * @return the managed XWiki object. + */ + Object getObjectApi(); + + /** + * @return the id of the managed XWiki object; + */ + int getObjectId(); +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XObjectDocumentDoesNotExistException.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XObjectDocumentDoesNotExistException.java new file mode 100644 index 000000000..f8b54438b --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XObjectDocumentDoesNotExistException.java @@ -0,0 +1,22 @@ +package com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes; + +import com.xpn.xwiki.XWikiException; + +/** + * Exception when try get {@link SuperDocument} that does not exist. + * + * @version $Id$ + * @since Application Manager 1.0RC1 + */ +public class XObjectDocumentDoesNotExistException extends XWikiException +{ + /** + * Create new instance of {@link XObjectDocumentDoesNotExistException}. + * + * @param message the error message. + */ + public XObjectDocumentDoesNotExistException(String message) + { + super(XWikiException.MODULE_XWIKI_DOC, XWikiException.ERROR_XWIKI_DOES_NOT_EXIST, message); + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/plugin/XWikiPluginMessageTool.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/plugin/XWikiPluginMessageTool.java new file mode 100644 index 000000000..0d1ee7f51 --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/core/plugin/XWikiPluginMessageTool.java @@ -0,0 +1,109 @@ +package com.xpn.xwiki.plugin.applicationmanager.core.plugin; + +import java.util.Collections; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.Arrays; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.plugin.XWikiPluginInterface; + +import com.xpn.xwiki.web.XWikiMessageTool; + +/** + * Plugin internationalization service based {@link XWikiMessageTool}. + * + * @version $Id$ + */ +public class XWikiPluginMessageTool extends XWikiMessageTool +{ + /** + * @param locale the locale. + * @param plugin the plugin. + * @param context the {@link com.xpn.xwiki.XWikiContext} object, used to get access to XWiki primitives for loading + * documents + */ + public XWikiPluginMessageTool(Locale locale, XWikiPluginInterface plugin, XWikiContext context) + { + this(ResourceBundle.getBundle(plugin.getName() + "/ApplicationResources", locale == null ? Locale.ENGLISH + : locale), context); + } + + /** + * @param bundle the default Resource Bundle to fall back to if no document bundle is found when trying to get a key + */ + public XWikiPluginMessageTool(ResourceBundle bundle) + { + this(bundle, null); + } + + /** + * @param bundle the default Resource Bundle to fall back to if no document bundle is found when trying to get a key + * @param context the {@link com.xpn.xwiki.XWikiContext} object, used to get access to XWiki primitives for loading + * documents + */ + public XWikiPluginMessageTool(ResourceBundle bundle, XWikiContext context) + { + super(bundle, context); + } + + /** + * {@inheritDoc} + *

+ * Start calling context's {@link XWikiMessageTool#get(String)} then if nothing is found use + * plugin's {@link ResourceBundle}. + * + * @see com.xpn.xwiki.web.XWikiMessageTool#getTranslation(java.lang.String) + */ + @Override + protected String getTranslation(String key) + { + String translation = key; + + if (context != null) { + translation = this.context.getMessageTool().get(key); + } + + // Want to know if XWikiMessageTool.get return exactly the provided key string (means it + // found nothing). + if (translation == key) { + try { + translation = this.bundle.getString(key); + } catch (Exception e) { + translation = null; + } + } + + return translation; + } + + /** + * Find a translation and then replace any parameters found in the translation by the passed params parameters. The + * format is the one used by {@link java.text.MessageFormat}. + * + * @param key the key of the string to find + * @param params the array of parameters to use for replacing "{N}" elements in the string. See + * {@link java.text.MessageFormat} for the full syntax + * @return the translated string with parameters resolved + * @see com.xpn.xwiki.web.XWikiMessageTool#get(String, List) + */ + public String get(String key, String[] params) + { + return get(key, Arrays.asList(params)); + } + + /** + * Find a translation and then replace any parameters found in the translation by the passed param parameter. The + * format is the one used by {@link java.text.MessageFormat}. + * + * @param key the key of the string to find + * @param param the parameter to use for replacing "{0}" element in the string. See {@link java.text.MessageFormat} + * for the full syntax + * @return the translated string with parameters resolved + * @see com.xpn.xwiki.web.XWikiMessageTool#get(String, List) + */ + public String get(String key, String param) + { + return get(key, Collections.singletonList(param)); + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/doc/XWikiApplication.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/doc/XWikiApplication.java new file mode 100644 index 000000000..c3e36769c --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/doc/XWikiApplication.java @@ -0,0 +1,753 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.doc; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.DefaultXObjectDocument; +import com.xpn.xwiki.doc.XWikiDocument; + +/** + * This class manage an XWiki document containing XApp.XWikiApplicationClass object. It add some specifics methods, + * getters and setters for this type of object and fields. + * + * @version $Id$ + */ +public class XWikiApplication extends DefaultXObjectDocument +{ + /** + * Pattern to match that indicate if a document name contains SQL "like" matching string. + */ + private static final Pattern EXT_DOCNAME_PATTERN = Pattern.compile("^\\[(.*)\\]$"); + + /** + * HQL where key word. + */ + private static final String HQL_WHERE = "where"; + + /** + * HQL or key word. + */ + private static final String HQL_OR = " or "; + + /** + * HQL and key word. + */ + private static final String HQL_AND = " and "; + + /** + * Filter to add in a named HQL query for a specific document. + */ + private static final String HQL_FILTER_DOC_EQUALS = "doc.fullName = ?"; + + /** + * Filter to add in a named HQL query to filter documents with a pattern. + */ + private static final String HQL_FILTER_DOC_PATTERN = "doc.fullName like ?"; + + /** + * Open HQL group. + */ + private static final String HQL_GROUP_OPEN = "("; + + /** + * Clause HQL group. + */ + private static final String HQL_GROUP_CLOSE = ")"; + + /** + * Create new XWikiApplication managing provided XWikiDocument. + * + * @param xdoc the encapsulated XWikiDocument + * @param objectId the id of the XWiki object included in the document to manage. + * @param context the XWiki context + * @throws XWikiException error when: + *

    + *
  • getting XWikiApplicationClass instance.
  • + *
  • or when calling {@link #reload(XWikiContext)}
  • + *
+ * @see DefaultXObjectDocument#DefaultXObjectDocument(com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.SuperClass, + * XWikiDocument, int, XWikiContext) + */ + public XWikiApplication(XWikiDocument xdoc, int objectId, XWikiContext context) throws XWikiException + { + super(XWikiApplicationClass.getInstance(context), xdoc, objectId, context); + } + + // /// + + /** + * @return the name of the application + * @see #setAppName(String) + */ + public String getAppName() + { + return getStringValue(XWikiApplicationClass.FIELD_APPNAME); + } + + /** + * Modify the name of the application. + * + * @param appname the new name of the application. + * @see #getAppName() + */ + public void setAppName(String appname) + { + setStringValue(XWikiApplicationClass.FIELD_APPNAME, appname); + } + + /** + * @return the pretty name of the application + * @see #setAppPrettyName(String) + */ + public String getAppPrettyName() + { + return getStringValue(XWikiApplicationClass.FIELD_APPPRETTYNAME); + } + + /** + * Modify the pratty name of the application. + * + * @param appprettyname the new pretty name of the application. + * @see #getAppPrettyName() + */ + public void setAppPrettyName(String appprettyname) + { + setStringValue(XWikiApplicationClass.FIELD_APPPRETTYNAME, appprettyname); + } + + /** + * @return the description of the application. + * @see #setDescription(String) + */ + public String getDescription() + { + return getStringValue(XWikiApplicationClass.FIELD_DESCRIPTION); + } + + /** + * Modify the description of the application. + * + * @param description the new description of the application. + * @see #getDescription() + */ + public void setDescription(String description) + { + setStringValue(XWikiApplicationClass.FIELD_DESCRIPTION, description); + } + + /** + * @return the version of the application. + * @see #setAppVersion(String) + */ + public String getAppVersion() + { + return getStringValue(XWikiApplicationClass.FIELD_APPVERSION); + } + + /** + * Modify the version of the application. + * + * @param appversion the version of the application. + * @see #getAppVersion() + */ + public void setAppVersion(String appversion) + { + setStringValue(XWikiApplicationClass.FIELD_APPVERSION, appversion); + } + + /** + * @return the authors of the application. + * @see #setAppAuthors(String) + */ + public String getAppAuthors() + { + return getStringValue(XWikiApplicationClass.FIELD_APPAUTHORS); + } + + /** + * Modify the authors of the application. + * + * @param appauthors the authors of the application. + * @see #getAppAuthors() + */ + public void setAppAuthors(String appauthors) + { + setStringValue(XWikiApplicationClass.FIELD_APPAUTHORS, appauthors); + } + + /** + * @return the license of the application. + * @see #setLicense(String) + */ + public String getLicense() + { + return getStringValue(XWikiApplicationClass.FIELD_LICENSE); + } + + /** + * Modify the version of the application. + * + * @param license the license of the application. + * @see #getAppVersion() + */ + public void setLicense(String license) + { + setStringValue(XWikiApplicationClass.FIELD_LICENSE, license); + } + + /** + * @return the list of plugins on which application depends. + * @see #setDependencies(List) + */ + public List getDependencies() + { + return getStringListValue(XWikiApplicationClass.FIELD_DEPENDENCIES); + } + + /** + * Modify the list of plugins on which application depends. + * + * @param dependencies the new list of plugins on which application depends. + * @see #getDependencies() + */ + public void setDependencies(List dependencies) + { + setStringListValue(XWikiApplicationClass.FIELD_DEPENDENCIES, dependencies); + } + + /** + * @return the list of other applications on which current application depends. + */ + public List getApplications() + { + return getStringListValue(XWikiApplicationClass.FIELD_APPLICATIONS); + } + + /** + * Modify the list of other applications on which current application depends. + * + * @param applications the new list of other applications on which current application depends. + * @see #getApplications() + */ + public void setApplications(List applications) + { + setStringListValue(XWikiApplicationClass.FIELD_APPLICATIONS, applications); + } + + /** + * @return the list of documents application contains. This method return the content of the field "documents", if + * you want the real list of documents names with resolved patterns and recursive tools you should use + * {@link #getDocumentsNames(boolean, boolean)}. + * @see #setDocuments(List) + */ + public List getDocuments() + { + return getStringListValue(XWikiApplicationClass.FIELD_DOCUMENTS); + } + + /** + * Modify the list of documents application contains. + * + * @param documents the new list of documents application contains. + * @see #getDocuments() + */ + public void setDocuments(List documents) + { + setStringListValue(XWikiApplicationClass.FIELD_DOCUMENTS, documents); + } + + /** + * @return the list of document application contains that will be included in place of copy from wiki template. This + * method return the content of the field "docstoinclude", if you want the real list of documents names with + * resolved patterns and recursive tools you should use {@link #getDocsNameToInclude(boolean)}. + * @see #setDocsToInclude(List) + */ + public List getDocsToInclude() + { + return getStringListValue(XWikiApplicationClass.FIELD_DOCSTOINCLUDE); + } + + /** + * Modify the list of document application contains that will be included in place of copy from wiki template. + * + * @param docstoinclude the new list of document application contains that will be included in place of copy from a + * wiki template. + * @see #getDocsToInclude() + */ + public void setDocsToInclude(List docstoinclude) + { + setStringListValue(XWikiApplicationClass.FIELD_DOCSTOINCLUDE, docstoinclude); + } + + /** + * @return the list of document application contains that will be linked in place of copy from a wiki template. This + * method return the content of the field "docstolink", if you want the real list of documents names with + * resolved patterns and recursive tools you should use {@link #getDocsNameToLink(boolean)}. + * @see #setDocsToLink(List) + */ + public List getDocsToLink() + { + return getStringListValue(XWikiApplicationClass.FIELD_DOCSTOLINK); + } + + /** + * Modify the list of document application contains that will be included in place of copy from wiki template. + * + * @param docstolink the new list of document application contains that will be linked in place of copy from a wiki + * template. + * @see #getDocsToLink() + */ + public void setDocsToLink(List docstolink) + { + setStringListValue(XWikiApplicationClass.FIELD_DOCSTOLINK, docstolink); + } + + /** + * @return the list of documents containing translations strings. + *

+ * Theses documents are added to XWiki.XWikiPreferences "documentBundles" field at application installation. + */ + public List getTranslationDocs() + { + return getStringListValue(XWikiApplicationClass.FIELD_TRANSLATIONDOCS); + } + + /** + * Modify the list of documents containing translations strings. + *

+ * Theses documents are added to XWiki.XWikiPreferences "documentBundles" field at application installation. + * + * @param translationdocs the new list of documents containing translations strings. Theses documents are added to + * XWiki.XWikiPreferences "documentBundles" field at application installation. + */ + public void setTranslationDocs(List translationdocs) + { + setStringListValue(XWikiApplicationClass.FIELD_TRANSLATIONDOCS, translationdocs); + } + + // /// + + /** + * {@inheritDoc} + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return getAppName() + "-" + getAppVersion(); + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + return getAppName() != null ? getAppName().hashCode() : "".hashCode(); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.api.Document#equals(java.lang.Object) + */ + @Override + public boolean equals(Object object) + { + boolean equals = false; + + if (object instanceof XWikiApplication) { + equals = + getAppName() == null ? ((XWikiApplication) object).getAppName() == null : getAppName() + .equalsIgnoreCase(((XWikiApplication) object).getAppName()); + } else if (object instanceof String) { + equals = getAppName() == null ? false : getAppName().equalsIgnoreCase((String) object); + } + + return equals; + } + + // /// + + /** + * Add all applications on which current application depend. + * + * @param rootApplication the root application containing recursively all in applicationList. + * @param applicationList the applications. + * @param recurse if true it add recursively all applications dependencies, if false return only direct + * dependencies. + * @param context the XWiki context. + * @throws XWikiException error when getting application descriptor document from the database. + * @see #getApplications() + */ + protected void addXWikiApplicationSet(XWikiApplication rootApplication, + Collection applicationList, boolean recurse, XWikiContext context) throws XWikiException + { + List applications = getApplications(); + for (String appname : applications) { + // Breaks infinite loop if application contains itself in its dependencies at any level. + if ((rootApplication == null || !rootApplication.equals(appname)) && !applicationList.contains(appname)) { + XWikiApplication app = ((XWikiApplicationClass) sclass).getApplication(appname, true, context); + applicationList.add(app); + + if (recurse) { + app.addXWikiApplicationSet(rootApplication, applicationList, recurse, context); + } + } + } + } + + /** + * Get set of XWikiApplication containing all applications on which current application depend. + * + * @param recurse if true it add recursively all applications dependencies, if false return only direct + * dependencies. + * @param context the XWiki context. + * @return the set list of XWikiApplication. + * @throws XWikiException error when getting application descriptor document from the database. + * @see #getApplications() + */ + public Set getXWikiApplicationSet(boolean recurse, XWikiContext context) throws XWikiException + { + Set applicationSet = new HashSet(); + + addXWikiApplicationSet(this, applicationSet, recurse, context); + + return applicationSet; + } + + /** + * Create a HQL where clause containing applications documents filter for provided documents type. + * + * @param applications the applications from which to get filters. + * @param type the XWikiApplicationClass field where to find documents names list : + * {@link XWikiApplicationClass#FIELD_DOCUMENTS}, {@link XWikiApplicationClass#FIELD_DOCSTOINCLUDE}, + * {@link XWikiApplicationClass#FIELD_DOCSTOLINK}. + * @param values the HQL values list filled for the named query. + * @param includeAppDesc if true application descriptor document names are included in the generated filter. + * @return a HQL where clause containing applications documents filter for provided documents type. + * @throws XWikiException error when creating HQL filter. + */ + private static String createApplicationsHqlFilter(Collection applications, String type, + Collection values, boolean includeAppDesc) throws XWikiException + { + StringBuffer filter = new StringBuffer(); + + for (XWikiApplication app : applications) { + String appFilter = app.createHqlFilter(type, values, false, includeAppDesc); + + if (!appFilter.equals("")) { + if (filter.length() > 0) { + filter.append(HQL_OR); + } + filter.append(HQL_GROUP_OPEN); + filter.append(appFilter); + filter.append(HQL_GROUP_CLOSE); + } + } + + return filter.toString(); + } + + /** + * Create a HQL where clause containing application documents filter for provided documents type. + * + * @param type the XWikiApplicationClass field where to find documents names list : + * {@link XWikiApplicationClass#FIELD_DOCUMENTS}, {@link XWikiApplicationClass#FIELD_DOCSTOINCLUDE}, + * {@link XWikiApplicationClass#FIELD_DOCSTOLINK}. + * @param values the HQL values list filled for the named query. + * @param recurse indicate if dependencies applications filters are included in the generated filter. + * @param includeAppDesc if true application descriptor document names are included in the generated filter. + * @return a HQL where clause containing application documents filter for provided documents type. + * @throws XWikiException error when creating HQL filter. + */ + private String createHqlFilter(String type, Collection values, boolean recurse, boolean includeAppDesc) + throws XWikiException + { + StringBuffer filter = new StringBuffer(); + + List patterns = getStringListValue(type); + + if (!patterns.isEmpty()) { + // Filter with applications documents + if (!type.equals(XWikiApplicationClass.FIELD_DOCUMENTS)) { + filter.append(HQL_GROUP_OPEN); + filter.append(createHqlFilter(getDocuments(), values, false)); + filter.append(HQL_GROUP_CLOSE); + } + + // Filter with provided applications documents type + String typeFilter = createHqlFilter(getStringListValue(type), values, includeAppDesc); + + if (!typeFilter.equals("")) { + if (filter.length() > 0) { + filter.append(HQL_AND); + } + + filter.append(HQL_GROUP_OPEN); + filter.append(typeFilter); + filter.append(HQL_GROUP_CLOSE); + } + } + + // Add dependencies applications hql filters for provided type + if (recurse) { + Collection applications = getXWikiApplicationSet(true, context); + + String dependenciesFilter = createApplicationsHqlFilter(applications, type, values, includeAppDesc); + + if (!dependenciesFilter.equals("")) { + if (filter.length() > 0) { + filter.append(HQL_OR); + } + filter.append(dependenciesFilter); + } + } + + return filter.toString(); + } + + /** + * Convert provided filter list in one hql where clause. + * + * @param docsNamesToResolve the application filters. + * @param values the HQL values list filled for the named query. + * @param includeAppDesc if true application descriptor document names are included in the generated filter. + * @return a HQL where clause containing application documents filter for provided documents type. + */ + private String createHqlFilter(Collection docsNamesToResolve, Collection values, + boolean includeAppDesc) + { + StringBuffer filter = new StringBuffer(); + + if (includeAppDesc) { + filter.append(HQL_FILTER_DOC_EQUALS); + values.add(this.getFullName()); + } + + for (String docName : docsNamesToResolve) { + if (filter.length() > 0) { + filter.append(HQL_OR); + } + + Matcher matcher = EXT_DOCNAME_PATTERN.matcher(docName); + if (matcher.matches()) { + // Add a pattern + filter.append(HQL_FILTER_DOC_PATTERN); + values.add(matcher.group(1)); + } else { + // Add a document name + filter.append(HQL_FILTER_DOC_EQUALS); + values.add(docName); + } + } + + return filter.toString(); + } + + /** + * Get and resolve all documents names of type type application contains. + *

+ * For each of these documents names, if are between "[" and "]", are considered as SQL matching string to use with + * "like". + * + * @param type type the XWikiApplicationClass field where to find documents names list : + * {@link XWikiApplicationClass#FIELD_DOCUMENTS}, {@link XWikiApplicationClass#FIELD_DOCSTOINCLUDE}, + * {@link XWikiApplicationClass#FIELD_DOCSTOLINK}. + * @param recurse if true it follow recursively all applications dependencies, if false parse only direct + * dependencies. + * @param includeAppDesc if true application descriptor document names is added to the returned set. + * @return all documents names of type type application contains. + * @throws XWikiException error when: + *

    + *
  • resolving SQL matching.
  • + *
  • or getting applications dependencies descriptors documents from the database.
  • + *
+ */ + private Set getDocsNamesByType(String type, boolean recurse, boolean includeAppDesc) throws XWikiException + { + List values = new ArrayList(); + + String where = createHqlFilter(type, values, recurse, includeAppDesc); + + return where.equals("") ? Collections. emptySet() : new HashSet(context.getWiki().getStore() + .searchDocumentsNames(HQL_WHERE + " " + where, values, context)); + } + + /** + * Get and resolve all documents names of type type provided applications contains. + *

+ * For each of these documents names, if are between "[" and "]", are considered as SQL matching string to use with + * "like". + * + * @param applications the applications from which to get documents names. + * @param type type the XWikiApplicationClass field where to find documents names list : + * {@link XWikiApplicationClass#FIELD_DOCUMENTS}, {@link XWikiApplicationClass#FIELD_DOCSTOINCLUDE}, + * {@link XWikiApplicationClass#FIELD_DOCSTOLINK}. + * @param includeAppDesc if true application descriptor document names is added to the returned set. + * @return all documents names of type type provided applications contains. + * @throws XWikiException error when resolving SQL matching. + */ + private static Set getApplicationsDocsNamesByType(Collection applications, String type, + boolean includeAppDesc) throws XWikiException + { + Set set = Collections.emptySet(); + if (applications.size() > 0) { + List values = new ArrayList(); + + String where = createApplicationsHqlFilter(applications, type, values, includeAppDesc); + + XWikiApplication app = applications.iterator().next(); + + if (where.equals("")) { + set = Collections.emptySet(); + } else { + set = + new HashSet(app.context.getWiki().getStore().searchDocumentsNames(HQL_WHERE + " " + where, + values, app.context)); + } + } + + return set; + } + + /** + * Get and resolve all documents names application contains. + *

+ * For each of these documents names, if are between "[" and "]", are considered as SQL matching string to use with + * "like". + * + * @param recurse if true it follow recursively all applications dependencies, if false parse only direct + * dependencies. + * @param includeAppDesc if true application descriptor document names is added to docsNames. + * @return all documents names application contains. + * @throws XWikiException error when: + *

    + *
  • resolving SQL matching.
  • + *
  • or getting applications dependencies descriptors documents from the database.
  • + *
+ * @see #getDocuments() + * @see XWikiApplicationClass#FIELD_DOCUMENTS + */ + public Set getDocumentsNames(boolean recurse, boolean includeAppDesc) throws XWikiException + { + return getDocsNamesByType(XWikiApplicationClass.FIELD_DOCUMENTS, recurse, includeAppDesc); + } + + /** + * Get and resolve all documents names to include application contains. + *

+ * For each of these documents names, if are between "[" and "]", are considered as SQL matching string to use with + * "like". + * + * @param recurse if true it follow recursively all applications dependencies, if false parse only direct + * dependencies. + * @return all documents names to include application contains. + * @throws XWikiException error when: + *

    + *
  • resolving SQL matching.
  • + *
  • or getting applications dependencies descriptors documents from the database.
  • + *
+ * @see #getDocsToInclude() + * @see XWikiApplicationClass#FIELD_DOCSTOINCLUDE + */ + public Set getDocsNameToInclude(boolean recurse) throws XWikiException + { + return getDocsNamesByType(XWikiApplicationClass.FIELD_DOCSTOINCLUDE, recurse, false); + } + + /** + * Get and resolve all documents names to include applications XWikiApplication list contains. + *

+ * For each of these documents names, if are between "[" and "]", are considered as SQL matching string to use with + * "like". + * + * @param applications the applications containing documents names to resolve and add to docsNames. + * @return all documents names to include applications contains. + * @throws XWikiException error when: + *

    + *
  • resolving SQL matching.
  • + *
  • or getting applications dependencies descriptors documents from the database.
  • + *
+ * @see #getDocsToInclude() + * @see XWikiApplicationClass#FIELD_DOCSTOINCLUDE + */ + public static Set getDocsNameToInclude(Collection applications) throws XWikiException + { + return getApplicationsDocsNamesByType(applications, XWikiApplicationClass.FIELD_DOCSTOINCLUDE, false); + } + + /** + * Get and resolve all documents names to link application contains. + *

+ * For each of these documents names, if are between "[" and "]", are considered as SQL matching string to use with + * "like". + * + * @param recurse if true it follow recursively all applications dependencies, if false parse only direct + * dependencies. + * @return all documents names to link application contains. + * @throws XWikiException error when: + *

    + *
  • resolving SQL matching.
  • + *
  • or getting applications dependencies descriptors documents from the database.
  • + *
+ * @see #getDocsToLink() + * @see XWikiApplicationClass#FIELD_DOCSTOLINK + */ + public Set getDocsNameToLink(boolean recurse) throws XWikiException + { + return getDocsNamesByType(XWikiApplicationClass.FIELD_DOCSTOLINK, recurse, false); + } + + /** + * Get and resolve all documents names to link applications XWikiApplication list contains. + *

+ * For each of these documents names, if are between "[" and "]", are considered as SQL matching string to use with + * "like". + * + * @param applications the applications containing documents names to resolve and add to docsNames. + * @return all documents names to link applications contains. + * @throws XWikiException error when: + *

    + *
  • resolving SQL matching.
  • + *
  • or getting applications dependencies descriptors documents from the database.
  • + *
+ * @see #getDocsToLink() + * @see XWikiApplicationClass#FIELD_DOCSTOLINK + */ + public static Set getDocsNameToLink(Collection applications) throws XWikiException + { + return getApplicationsDocsNamesByType(applications, XWikiApplicationClass.FIELD_DOCSTOLINK, false); + } +} diff --git a/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/doc/XWikiApplicationClass.java b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/doc/XWikiApplicationClass.java new file mode 100644 index 000000000..6b91ee6bb --- /dev/null +++ b/celements-xapp/component/src/main/java/com/xpn/xwiki/plugin/applicationmanager/doc/XWikiApplicationClass.java @@ -0,0 +1,473 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.doc; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.celements.model.access.IModelAccessFacade; +import com.celements.model.context.ModelContext; +import com.celements.model.util.ModelUtils; +import com.xpn.xwiki.XWiki; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.objects.BaseObject; +import com.xpn.xwiki.objects.StringProperty; +import com.xpn.xwiki.objects.classes.BaseClass; +import com.xpn.xwiki.plugin.applicationmanager.ApplicationManagerException; +import com.xpn.xwiki.plugin.applicationmanager.ApplicationManagerMessageTool; +import com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.AbstractXClassManager; +import com.xpn.xwiki.web.Utils; + +/** + * {@link com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager} + * implementation for + * XAppClasses.XWikiApplicationClass class. + * + * @version $Id$ + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.AbstractXClassManager + */ +@Component +public class XWikiApplicationClass extends AbstractXClassManager { + + private static final Logger logger = LoggerFactory.getLogger(XWikiApplicationClass.class); + /** + * Default list display type of XAppClasses.XWikiApplicationClass fields. + */ + public static final String DEFAULT_FIELDDT = "input"; + + /** + * Default list separators of XAppClasses.XWikiApplicationClass fields. + */ + public static final String DEFAULT_FIELDS = "|"; + + /** + * Name of field appname for the XWiki class XAppClasses.XWikiApplicationClass. The + * unique name of the + * application. + */ + public static final String FIELD_APPNAME = "appname"; + + /** + * Pretty name of field appname for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_APPNAME = "Application Name"; + + /** + * Name of field appprettyname for the XWiki class XAppClasses.XWikiApplicationClass. + * The displayed + * name of the application. + */ + public static final String FIELD_APPPRETTYNAME = "appprettyname"; + + /** + * Pretty name of field appprettyname for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_APPPRETTYNAME = "Application Pretty Name"; + + /** + * Name of field description for the XWiki class XAppClasses.XWikiApplicationClass. + * The description of + * the application. + */ + public static final String FIELD_DESCRIPTION = "description"; + + /** + * Pretty name of field description for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_DESCRIPTION = "Description"; + + /** + * Name of field version for the XWiki class XAppClasses.XWikiApplicationClass. The + * version of the + * application. + */ + public static final String FIELD_APPVERSION = "appversion"; + + /** + * Pretty name of field version for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_APPVERSION = "Application Version"; + + /** + * Name of field appauthors for the XWiki class XAppClasses.XWikiApplicationClass. + * The description of + * the application. + */ + public static final String FIELD_APPAUTHORS = "appauthors"; + + /** + * Pretty name of field appauthors for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_APPAUTHORS = "Authors"; + + /** + * Name of field license for the XWiki class XAppClasses.XWikiApplicationClass. The + * description of the + * application. + */ + public static final String FIELD_LICENSE = "license"; + + /** + * Pretty name of field license for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_LICENSE = "License"; + + /** + * Name of field dependencies for the XWiki class XAppClasses.XWikiApplicationClass. + * The list of + * plugins on which application depends. + */ + public static final String FIELD_DEPENDENCIES = "dependencies"; + + /** + * Pretty name of field dependencies for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_DEPENDENCIES = "Dependencies"; + + /** + * Name of field applications for the XWiki class XAppClasses.XWikiApplicationClass. + * The list of other + * applications on which current application depends. + */ + public static final String FIELD_APPLICATIONS = "applications"; + + /** + * Pretty name of field applications for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_APPLICATIONS = "Applications"; + + /** + * Name of field documents for the XWiki class XAppClasses.XWikiApplicationClass. The + * list of documents + * application contains. + */ + public static final String FIELD_DOCUMENTS = "documents"; + + /** + * Pretty name of field documents for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_DOCUMENTS = "Documents"; + + /** + * Name of field docstoinclude for the XWiki class XAppClasses.XWikiApplicationClass. + * The list of + * document application contains that will be included in place of copy from wiki template. + */ + public static final String FIELD_DOCSTOINCLUDE = "docstoinclude"; + + /** + * Pretty name of field docstoinclude for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_DOCSTOINCLUDE = "Documents to include"; + + /** + * Name of field docstolink for the XWiki class XAppClasses.XWikiApplicationClass. + * The list of document + * application contains that will be linked in place of copy from wiki template. + */ + public static final String FIELD_DOCSTOLINK = "docstolink"; + + /** + * Pretty name of field docstolink for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_DOCSTOLINK = "Documents to link"; + + /** + * Name of field translationdocs for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELD_TRANSLATIONDOCS = "translationdocs"; + + /** + * Pretty name of field translationdocs for the XWiki class + * XAppClasses.XWikiApplicationClass. + */ + public static final String FIELDPN_TRANSLATIONDOCS = "Translations documents"; + + // /// + + /** + * Space of class document. + */ + private static final String CLASS_SPACE_PREFIX = "XApp"; + + /** + * Prefix of class document. + */ + private static final String CLASS_PREFIX = "XWikiApplication"; + + /** + * The default parent page of an application descriptor document. + */ + private static final String DEFAULT_APPLICATION_PARENT = CLASS_SPACE_PREFIX + "Manager.WebHome"; + + /** + * The default application version of an application descriptor document. + */ + private static final String DEFAULT_APPLICATION_VERSION = "1.0"; + + /** + * Construct the overload of AbstractXClassManager with spaceprefix={@link #CLASS_SPACE_PREFIX} + * and prefix= + * {@link #CLASS_PREFIX}. + */ + @Inject + protected XWikiApplicationClass(IModelAccessFacade modelAccess, ModelContext mContext, + ModelUtils modelUtils) { + super(CLASS_SPACE_PREFIX, CLASS_PREFIX, modelAccess, mContext, modelUtils); + } + + /** + * Return unique instance of XWikiApplicationClass and update documents for this context. It also + * check if the + * corresponding XWiki class/template/sheet exist in context's database and create it if not. + * + * @param context + * the XWiki context. + * @param check + * indicate if class existence has to be checked in the wiki. + * @return a unique instance of XWikiApplicationClass. + * @throws XWikiException + * error when checking for class, class template and class sheet. + */ + public static XWikiApplicationClass getInstance(XWikiContext context, boolean check) + throws XWikiException { + XWikiApplicationClass xappService = Utils.getComponent(XWikiApplicationClass.class); + if (check) { + xappService.check(context); + } + + return xappService; + } + + /** + * Return unique instance of XWikiApplicationClass and update documents for this context. It also + * check if the + * corresponding Xwiki class/template/sheet exist in context's database and create it if not. + * + * @param context + * the XWiki context. + * @return a unique instance of XWikiApplicationClass. + * @throws XWikiException + * error when checking for class, class template and class sheet. + */ + public static XWikiApplicationClass getInstance(XWikiContext context) throws XWikiException { + return getInstance(context, true); + } + + /** + * Indicate if the provided document contains application descriptor. + * + * @param doc + * the document. + * @return true if the document contains an application descriptor, false otherwise. + */ + public static boolean isApplication(XWikiDocument doc) { + boolean isApplication = false; + + try { + XWikiApplicationClass xclass = getInstance(null, false); + isApplication = xclass.isInstance(doc); + } catch (XWikiException e) { + logger.error("Fail to get unique instance of " + XWikiApplicationClass.class.getName(), e); + } + + return isApplication; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.AbstractXClassManager#updateBaseClass(com.xpn.xwiki.objects.classes.BaseClass) + */ + @Override + protected boolean updateBaseClass(BaseClass baseClass) { + boolean needsUpdate = super.updateBaseClass(baseClass); + + needsUpdate |= baseClass.addTextField(FIELD_APPNAME, FIELDPN_APPNAME, 80); + needsUpdate |= baseClass.addTextField(FIELD_APPPRETTYNAME, FIELDPN_APPPRETTYNAME, 30); + needsUpdate |= baseClass.addTextAreaField(FIELD_DESCRIPTION, FIELDPN_DESCRIPTION, 40, 5); + needsUpdate |= baseClass.addTextField(FIELD_APPVERSION, FIELDPN_APPVERSION, 30); + + needsUpdate |= baseClass.addTextField(FIELD_APPAUTHORS, FIELDPN_APPAUTHORS, 30); + + needsUpdate |= baseClass.addTextField(FIELD_LICENSE, FIELDPN_LICENSE, 30); + + needsUpdate |= baseClass.addStaticListField(FIELD_DEPENDENCIES, FIELDPN_DEPENDENCIES, 80, true, + "", DEFAULT_FIELDDT, + DEFAULT_FIELDS); + + needsUpdate |= baseClass.addStaticListField(FIELD_APPLICATIONS, FIELDPN_APPLICATIONS, 80, true, + "", DEFAULT_FIELDDT, + DEFAULT_FIELDS); + + needsUpdate |= baseClass.addStaticListField(FIELD_DOCUMENTS, FIELDPN_DOCUMENTS, 80, true, "", + DEFAULT_FIELDDT, + DEFAULT_FIELDS); + + needsUpdate |= baseClass.addStaticListField(FIELD_DOCSTOINCLUDE, FIELDPN_DOCSTOINCLUDE, 80, + true, "", DEFAULT_FIELDDT, + DEFAULT_FIELDS); + + needsUpdate |= baseClass.addStaticListField(FIELD_DOCSTOLINK, FIELDPN_DOCSTOLINK, 80, true, "", + DEFAULT_FIELDDT, + DEFAULT_FIELDS); + + needsUpdate |= baseClass.addStaticListField(FIELD_TRANSLATIONDOCS, FIELDPN_TRANSLATIONDOCS, 80, + true, "", DEFAULT_FIELDDT, + DEFAULT_FIELDS); + + return needsUpdate; + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.AbstractXClassManager#updateClassTemplateDocument(com.xpn.xwiki.doc.XWikiDocument) + */ + @Override + protected boolean updateClassTemplateDocument(XWikiDocument doc) { + boolean needsUpdate = false; + + if (!(DEFAULT_APPLICATION_PARENT).equals(doc.getParent())) { + doc.setParent(DEFAULT_APPLICATION_PARENT); + needsUpdate = true; + } + + needsUpdate |= updateDocStringValue(doc, FIELD_APPVERSION, DEFAULT_APPLICATION_VERSION); + + return needsUpdate; + } + + /** + * Get the XWiki document descriptor of containing XAppClasses.XWikiApplication XWiki object with + * "appname" field + * equals to appName. + * + * @param appName + * the name of the application. + * @param context + * the XWiki context. + * @param validate + * indicate if it return new {@link XWikiDocument} or throw exception if application + * descriptor does + * not exist. + * @return the {@link XWikiDocument} representing application descriptor. + * @throws XWikiException + * error when searching for application descriptor document. + * @see #getApplication(String, XWikiContext, boolean) + */ + protected XWikiDocument getApplicationDocument(String appName, XWikiContext context, + boolean validate) + throws XWikiException { + XWiki xwiki = context.getWiki(); + + String[][] fieldDescriptors = new String[][] { + { FIELD_APPNAME, StringProperty.class.getSimpleName(), appName } }; + List parameterValues = new ArrayList<>(); + String where = createWhereClause(fieldDescriptors, parameterValues); + + List listApp = context.getWiki().getStore().searchDocuments(where, + parameterValues, context); + + if (listApp.isEmpty()) { + if (validate) { + throw new ApplicationManagerException(ApplicationManagerException.ERROR_AM_DOESNOTEXIST, + ApplicationManagerMessageTool.getDefault(context).get( + ApplicationManagerMessageTool.ERROR_APPDOESNOTEXISTS, appName)); + } else { + return xwiki.getDocument(getItemDocumentDefaultFullName(appName, context), context); + } + } + + return listApp.get(0); + } + + /** + * Get the XWiki document descriptor of containing XAppClasses.XWikiApplication XWiki object with + * "appname" field + * equals to appName. + * + * @param appName + * the name of the application. + * @param context + * the XWiki context. + * @param validate + * indicate if it return new XWikiDocument or throw exception if application descriptor + * does not + * exist. + * @return the XWikiApplication representing application descriptor. + * @throws XWikiException + * error when searching for application descriptor document. + * @see #getApplicationDocument(String, XWikiContext, boolean) + */ + public XWikiApplication getApplication(String appName, boolean validate, XWikiContext context) + throws XWikiException { + XWikiDocument doc = getApplicationDocument(appName, context, validate); + + int objectId = 0; + for (BaseObject obj : doc.getObjects(getClassFullName())) { + if (obj.getStringValue(FIELD_APPNAME).equalsIgnoreCase(appName)) { + break; + } + + ++objectId; + } + + if (objectId == doc.getObjects(getClassFullName()).size()) { + objectId = 0; + } + + return newXObjectDocument(doc, objectId, context); + } + + /** + * {@inheritDoc} + * + * @see com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.AbstractXClassManager#newXObjectDocument(com.xpn.xwiki.doc.XWikiDocument, + * int, com.xpn.xwiki.XWikiContext) + */ + @Override + public XWikiApplication newXObjectDocument(XWikiDocument doc, int objId, XWikiContext context) + throws XWikiException { + return new XWikiApplication(doc, objId, context); + } +} diff --git a/celements-xapp/component/src/main/resources/applicationmanager/ApplicationResources.properties b/celements-xapp/component/src/main/resources/applicationmanager/ApplicationResources.properties new file mode 100644 index 000000000..8188a79a3 --- /dev/null +++ b/celements-xapp/component/src/main/resources/applicationmanager/ApplicationResources.properties @@ -0,0 +1,27 @@ +# comments +applicationmanager.plugin.comment.createapplication=Create application [{0}] +applicationmanager.plugin.comment.importapplication=Import application [{0}] +applicationmanager.plugin.comment.reloadapplication=Reload application [{0}] +applicationmanager.plugin.comment.reloadallapplications=Reload all applications +applicationmanager.plugin.comment.autoupdatetranslations=Automatically update translations informations from applications in [{0}] +applicationmanager.plugin.comment.refreshalltranslations=Refresh all applications translations informations + +# messages +applicationmanager.plugin.error.applicationpagealreadyexists=Application [{0}] descriptor page already exists +applicationmanager.plugin.error.import.packagedoesnotexists=Package [{0}] does not exists +applicationmanager.plugin.error.import.import=Fail to import package [{0}] +applicationmanager.plugin.error.import.install=Fail to install package [{0}] +applicationmanager.plugin.error.applicationdoesnotexists=Application [{0}] does not exists + +# log +applicationmanager.plugin.log.createapplication=Application creation failed [{0}] +applicationmanager.plugin.log.deleteapplication=Application [{0}] failed +applicationmanager.plugin.log.getallapplications=Get all applications descriptors failed +applicationmanager.plugin.log.getapplication=Get application descriptor failed +applicationmanager.plugin.log.exportapplication=Application [{0}] export as a XAR package failed +applicationmanager.plugin.log.importapplication=Application import from XAR package [{0}] failed +applicationmanager.plugin.log.reloadapplication=Application [{0}] reload failed +applicationmanager.plugin.log.realoadallapplications=All applications reload failed +applicationmanager.plugin.log.getrootapplication=Get wiki root application failed +applicationmanager.plugin.log.autoupdatetranslations=Error when automatically updating translations informations from applications in [{0}] +applicationmanager.plugin.log.refreshalltranslations=Refresh all applications translations informations \ No newline at end of file diff --git a/celements-xapp/component/src/main/resources/sheets/XAppSheets.XWikiApplicationClassSheet.vm b/celements-xapp/component/src/main/resources/sheets/XAppSheets.XWikiApplicationClassSheet.vm new file mode 100644 index 000000000..cf340b355 --- /dev/null +++ b/celements-xapp/component/src/main/resources/sheets/XAppSheets.XWikiApplicationClassSheet.vm @@ -0,0 +1,61 @@ +#set($Appmanager = $xwiki.applicationmanager) +#set($Exception = $Appmanager.DefaultException) +## +#set($object = $doc.getObject("XAppClasses.XWikiApplicationClass")) +## +#if($object) + #set($class = $object.xWikiClass) + #set($appname = $doc.getValue("appname", $object)) +#end +## +############################################################################# +## +#if($appname && $appname.length() > 0) + ## + #set($app = $Appmanager.getApplicationDocument($doc.getValue("appname", $object))) + ## + #if($context.action == "view") + 1 Application \"#if($app.appprettyname && $app.appprettyname.trim() != "")$app.appprettyname#else$app.appname#end\" + #end + ## +
+ #foreach($prop in $class.properties) +
${prop.prettyName}
+ #if($context.action == "view" && $prop.name == "documents") +
+ #foreach($docfullname in $app.getDocumentsNames(false, false)) + [$docfullname>$docfullname] + #end +
+ #elseif($context.action == "view" && $prop.name == "docstoinclude") +
+ #foreach($docfullname in $app.getDocsNameToInclude(false)) + [$docfullname>$docfullname] + #end +
+ #elseif($context.action == "view" && $prop.name == "docstolink") +
+ #foreach($docfullname in $app.getDocsNameToLink(false)) + [$docfullname>$docfullname] + #end +
+ #elseif($context.action == "view" && $prop.name == "translationdocs") +
+ #foreach($docfullname in $app.getTranslationDocs()) + [$docfullname>$docfullname] + #end +
+ #elseif($context.action == "view" && $prop.name == "applications") +
+ #foreach($appname in $app.getApplications()) + [$appname>$Appmanager.getApplicationDocument($appname).fullName] + #end +
+ #else +
$doc.display($prop.getName())
+ #end + #end +
+#else + 1 Document \"$doc.name\" +#end \ No newline at end of file diff --git a/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/api/XWikiExceptionApiTest.java b/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/api/XWikiExceptionApiTest.java new file mode 100644 index 000000000..c38d10b1f --- /dev/null +++ b/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/api/XWikiExceptionApiTest.java @@ -0,0 +1,105 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.core.api; + +import junit.framework.TestCase; +import com.xpn.xwiki.XWikiException; + +/** + * Unit tests for {@link com.xpn.xwiki.plugin.applicationmanager.core.api.XWikiExceptionApi}. + * + * @version $Id$ + */ +public class XWikiExceptionApiTest extends TestCase +{ + /** + * We only verify here we get the correct error code for some static error field name. + * + * @throws XWikiException error occurred. + */ + public void testGetStaticErrorCode() throws XWikiException + { + XWikiExceptionApi exceptionapi = new XWikiExceptionApi(new XWikiException(), null); + + assertEquals("Error code is incorrect", XWikiException.ERROR_XWIKI_UNKNOWN, exceptionapi + .get("ERROR_XWIKI_UNKNOWN")); + assertEquals("Error code is incorrect", XWikiException.ERROR_XWIKI_EXPORT_XSL_FILE_NOT_FOUND, exceptionapi + .get("ERROR_XWIKI_EXPORT_XSL_FILE_NOT_FOUND")); + assertEquals("Error code is incorrect", XWikiException.ERROR_XWIKI_CONTENT_LINK_INVALID_URI, exceptionapi + .get("ERROR_XWIKI_CONTENT_LINK_INVALID_URI")); + } + + /** + * Try to get error code that does not exists. + */ + public void testCantFindErrorCode() + { + XWikiExceptionApi exceptionapi = new XWikiExceptionApi(new XWikiException(), null); + + try { + exceptionapi.get("wrongerrorname"); + fail("XWikiExceptionApi.get(String) did not throwed exception with \"wrongerrorname\" " + "error name."); + } catch (XWikiException e) { + assertEquals("Wrong exception throwed.", XWikiExceptionApi.ERROR_XWIKI_ERROR_DOES_NOT_EXIST, e.getCode()); + } + } + + /** + * Example of XWikiException overload. + */ + public class SomeExtendedException extends XWikiException + { + public static final int ERROR_EXTENDED_ERROR1 = 80001; + + public static final int ERROR_EXTENDED_ERROR2 = 80002; + } + + /** + * Example of XWikiException overload. + */ + public class SomeOtherExtendedException extends XWikiException + { + public static final int ERROR_EXTENDED_ERROR1 = 90001; + + public static final int ERROR_EXTENDED_ERROR2 = 90002; + } + + /** + * We only verify here we get the correct error code for some static error field name. + * + * @throws XWikiException error occurred. + */ + public void testGetExtendedStaticErrorCode() throws XWikiException + { + XWikiExceptionApi exceptionapi1 = new XWikiExceptionApi(new SomeExtendedException(), null); + + assertEquals("Error code is incorrect", SomeExtendedException.ERROR_EXTENDED_ERROR1, exceptionapi1 + .get("ERROR_EXTENDED_ERROR1")); + assertEquals("Error code is incorrect", SomeExtendedException.ERROR_EXTENDED_ERROR2, exceptionapi1 + .get("ERROR_EXTENDED_ERROR2")); + + XWikiExceptionApi exceptionapi2 = new XWikiExceptionApi(new SomeOtherExtendedException(), null); + + assertEquals("Error code is incorrect", SomeOtherExtendedException.ERROR_EXTENDED_ERROR1, exceptionapi2 + .get("ERROR_EXTENDED_ERROR1")); + assertEquals("Error code is incorrect", SomeOtherExtendedException.ERROR_EXTENDED_ERROR2, exceptionapi2 + .get("ERROR_EXTENDED_ERROR2")); + } +} diff --git a/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/DefaultXObjectDocumentTest.java b/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/DefaultXObjectDocumentTest.java new file mode 100644 index 000000000..eba9b7d1b --- /dev/null +++ b/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/DefaultXObjectDocumentTest.java @@ -0,0 +1,245 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes; + +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.validation.constraints.NotNull; + +import org.easymock.IAnswer; +import org.junit.Before; +import org.junit.Test; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.rendering.syntax.Syntax; + +import com.celements.common.test.AbstractComponentTest; +import com.celements.model.access.IModelAccessFacade; +import com.celements.model.access.exception.DocumentNotExistsException; +import com.celements.model.context.ModelContext; +import com.celements.model.reference.RefBuilder; +import com.celements.model.util.ModelUtils; +import com.xpn.xwiki.XWiki; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.user.api.XWikiRightService; +import com.xpn.xwiki.web.Utils; + +/** + * Unit tests for + * {@link com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.DefaultXObjectDocument}. + * + * @version $Id$ + */ +public class DefaultXObjectDocumentTest extends AbstractComponentTest { + + private XWiki xwiki; + + private Map documents = new HashMap<>(); + + private IModelAccessFacade modelAccessMock; + private ModelContext mContext; + private ModelUtils modelUtils; + + /** + * {@inheritDoc} + * + * @see junit.framework.TestCase#setUp() + */ + @Before + public void prepare() throws Exception { + mContext = Utils.getComponent(ModelContext.class); + modelUtils = Utils.getComponent(ModelUtils.class); + DEFAULT_DOC_REF = RefBuilder.from(mContext.getWikiRef()).space(DEFAULT_SPACE) + .doc(DEFAULT_DOCFULLNAME).build(DocumentReference.class); + modelAccessMock = registerComponentMock(IModelAccessFacade.class); + mockGetDocAndSaveDoc(); + xwiki = getMock(XWiki.class); + mockGetXClass(); + } + + // ///////////////////////////////////////////////////////////////////////////////////////: + // Tests + + private final String DEFAULT_SPACE = "Space"; + + private final String DEFAULT_DOCNAME = "Document"; + + private final String DEFAULT_DOCFULLNAME = DEFAULT_SPACE + "." + DEFAULT_DOCNAME; + private DocumentReference DEFAULT_DOC_REF; + + @Test + public void testInitXObjectDocumentEmpty() throws XWikiException { + documents.clear(); + replayDefault(); + XClassManager sclass = XClassManagerTest.DispatchXClassManager.getInstance( + modelAccessMock, + mContext, modelUtils); + DefaultXObjectDocument sdoc = (DefaultXObjectDocument) sclass.newXObjectDocument(getXContext()); + verifyDefault(); + assertNotNull(sdoc); + assertTrue(sdoc.isNew()); + + com.xpn.xwiki.api.Object obj = sdoc.getObject(sclass.getClassFullName()); + + assertNotNull(obj); + assertEquals(sdoc.getXClassManager(), sclass); + } + + @Test + public void testInitXObjectDocumentDocName() throws XWikiException { + documents.clear(); + replayDefault(); + XClassManager sclass = XClassManagerTest.DispatchXClassManager.getInstance( + modelAccessMock, + mContext, modelUtils); + DefaultXObjectDocument sdoc = (DefaultXObjectDocument) sclass + .newXObjectDocument(DEFAULT_DOC_REF, 0, getXContext()); + verifyDefault(); + assertNotNull(sdoc); + assertTrue(sdoc.isNew()); + + com.xpn.xwiki.api.Object obj = sdoc.getObject(sclass.getClassFullName()); + + assertNotNull(obj); + assertEquals(sdoc.getXClassManager(), sclass); + } + + @Test + public void testInitXObjectDocumentDocNameExists() throws Exception { + documents.clear(); + replayDefault(); + XWikiDocument doc = modelAccessMock.getOrCreateDocument(DEFAULT_DOC_REF); + modelAccessMock.saveDocument(doc); + + XClassManager sclass = XClassManagerTest.DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils); + DefaultXObjectDocument sdoc = (DefaultXObjectDocument) sclass + .newXObjectDocument(DEFAULT_DOC_REF, 0, getXContext()); + verifyDefault(); + assertNotNull(sdoc); + assertTrue(sdoc.isNew()); + + com.xpn.xwiki.api.Object obj = sdoc.getObject(sclass.getClassFullName()); + + assertNotNull(obj); + assertEquals(sdoc.getXClassManager(), sclass); + } + + @Test + public void testMergeObject() throws XWikiException { + replayDefault(); + XClassManager sclass = XClassManagerTest.DispatchXClassManager.getInstance( + modelAccessMock, mContext, modelUtils); + DefaultXObjectDocument sdoc1 = (DefaultXObjectDocument) sclass + .newXObjectDocument(getXContext()); + + DefaultXObjectDocument sdoc2 = (DefaultXObjectDocument) sclass + .newXObjectDocument(getXContext()); + + sdoc1.setStringValue(XClassManagerTest.FIELD_string, "valuesdoc1"); + sdoc1.setStringValue(XClassManagerTest.FIELD_string2, "value2sdoc1"); + + sdoc2.setStringValue(XClassManagerTest.FIELD_string, "valuesdoc2"); + sdoc2.setIntValue(XClassManagerTest.FIELD_int, 2); + + sdoc1.mergeObject(sdoc2); + verifyDefault(); + assertEquals("The field is not overwritten", + sdoc1.getStringValue(XClassManagerTest.FIELD_string), + sdoc2.getStringValue(XClassManagerTest.FIELD_string)); + assertEquals("The field is removed", "value2sdoc1", sdoc1 + .getStringValue(XClassManagerTest.FIELD_string2)); + assertEquals("The field is not added", sdoc1.getIntValue(XClassManagerTest.FIELD_int), sdoc1 + .getIntValue(XClassManagerTest.FIELD_int)); + } + + /******************* + * HELPER METHODS + *******************/ + + private @NotNull XWikiDocument stubGetDocument(DocumentReference theDocRef) + throws DocumentNotExistsException { + if (documents.containsKey(theDocRef)) { + return documents.get(theDocRef); + } else { + throw new DocumentNotExistsException(theDocRef); + } + } + + private @NotNull XWikiDocument stubGetOrCreateDocument(DocumentReference theDocRef) + throws DocumentNotExistsException { + if (documents.containsKey(theDocRef)) { + return documents.get(theDocRef); + } else { + XWikiDocument doc = new XWikiDocument(theDocRef); + doc.setNew(true); + doc.setLanguage(DEFAULT_LANG); + doc.setDefaultLanguage(DEFAULT_LANG); + doc.setTranslation(0); + Date creationDate = new Date(); + doc.setCreationDate(creationDate); + doc.setContentUpdateDate(creationDate); + doc.setDate(creationDate); + doc.setCreator(XWikiRightService.SUPERADMIN_USER); + doc.setAuthor(XWikiRightService.SUPERADMIN_USER); + doc.setContentAuthor(XWikiRightService.SUPERADMIN_USER); + doc.setContent(""); + doc.setContentDirty(true); + doc.setMetaDataDirty(true); + doc.setOriginalDocument(new XWikiDocument(doc.getDocumentReference())); + doc.setSyntax(Syntax.XWIKI_1_0); + return doc; + } + } + + private void mockGetDocAndSaveDoc() throws Exception { + expect(modelAccessMock.getDocument(isA(DocumentReference.class))) + .andAnswer(() -> stubGetDocument(getCurrentArgument(0))).anyTimes(); + expect(modelAccessMock.getOrCreateDocument(isA(DocumentReference.class))) + .andAnswer(() -> stubGetOrCreateDocument(getCurrentArgument(0))).anyTimes(); + IAnswer saveDocStub = () -> { + XWikiDocument theDoc = getCurrentArgument(0); + theDoc.setNew(false); + documents.put(theDoc.getDocumentReference(), theDoc); + return null; + }; + modelAccessMock.saveDocument(isA(XWikiDocument.class)); + expectLastCall().andAnswer(saveDocStub).anyTimes(); + modelAccessMock.saveDocument(isA(XWikiDocument.class), isA(String.class)); + expectLastCall().andAnswer(saveDocStub).anyTimes(); + modelAccessMock.saveDocument(isA(XWikiDocument.class), isA(String.class), anyBoolean()); + expectLastCall().andAnswer(saveDocStub).anyTimes(); + } + + private void mockGetXClass() throws Exception { + expect(xwiki.getXClass(isA(DocumentReference.class), same(getXContext()))) + .andAnswer(() -> { + DocumentReference classReference = getCurrentArgument(0); + XWikiDocument doc = stubGetDocument(classReference); + return doc.getxWikiClass(); + }).anyTimes(); + } +} diff --git a/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XClassManagerTest.java b/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XClassManagerTest.java new file mode 100644 index 000000000..bdabc4090 --- /dev/null +++ b/celements-xapp/component/src/test/java/com/xpn/xwiki/plugin/applicationmanager/core/doc/objects/classes/XClassManagerTest.java @@ -0,0 +1,775 @@ +/* + * 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 com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes; + +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.validation.constraints.NotNull; + +import org.easymock.IAnswer; +import org.junit.Before; +import org.junit.Test; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.rendering.syntax.Syntax; + +import com.celements.common.test.AbstractComponentTest; +import com.celements.model.access.IModelAccessFacade; +import com.celements.model.access.exception.DocumentNotExistsException; +import com.celements.model.context.ModelContext; +import com.celements.model.util.ModelUtils; +import com.xpn.xwiki.XWiki; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.api.Document; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.objects.BaseObject; +import com.xpn.xwiki.objects.PropertyInterface; +import com.xpn.xwiki.objects.classes.BaseClass; +import com.xpn.xwiki.user.api.XWikiRightService; +import com.xpn.xwiki.web.Utils; + +/** + * Unit tests for + * {@link com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.AbstractXClassManager}. + * + * @version $Id$ + */ +public class XClassManagerTest extends AbstractComponentTest { + + private XWiki xwiki; + + private Map documents = new HashMap<>(); + + private IModelAccessFacade modelAccessMock; + private ModelContext mContext; + private ModelUtils modelUtils; + + /** + * {@inheritDoc} + * + * @see junit.framework.TestCase#setUp() + */ + @Before + public void prepare() throws Exception { + modelAccessMock = registerComponentMock(IModelAccessFacade.class); + mContext = Utils.getComponent(ModelContext.class); + modelUtils = Utils.getComponent(ModelUtils.class); + mockGetDocAndSaveDoc(); + xwiki = getMock(XWiki.class); + mockClearName(); + mockGetXClass(); + /* + * is getClass deprecated?!? + * mockXWiki.stubs().method("getClass").will(new CustomStub("Implements XWiki.getClass") { + * + * @Override + * public Object invoke(Invocation invocation) throws Throwable { + * String classFullName = (String) invocation.parameterValues.get(0); + * XWikiContext context = (XWikiContext) invocation.parameterValues.get(1); + * + * XWikiDocument doc = context.getWiki().getDocument(classFullName, context); + * + * return doc.getxWikiClass(); + * } + * }); + */ + } + + // ///////////////////////////////////////////////////////////////////////////////////////: + // Tests + + private static final String CLASS_SPACE_PREFIX = "Space"; + + private static final String CLASS_PREFIX = "Prefix"; + + private static final String CLASS_NAME = CLASS_PREFIX + + com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager.XWIKI_CLASS_SUFFIX; + + private static final String CLASSSHEET_NAME = CLASS_PREFIX + + com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager.XWIKI_CLASSSHEET_SUFFIX; + + private static final String CLASSTEMPLATE_NAME = CLASS_PREFIX + + com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager.XWIKI_CLASSTEMPLATE_SUFFIX; + + private static final String DISPATCH_CLASS_SPACE = CLASS_SPACE_PREFIX + + com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager.XWIKI_CLASS_SPACE_SUFFIX; + + private static final String DISPATCH_CLASS_FULLNAME = DISPATCH_CLASS_SPACE + "." + CLASS_NAME; + + private static final String DISPATCH_CLASSSHEET_SPACE = CLASS_SPACE_PREFIX + + com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager.XWIKI_CLASSSHEET_SPACE_SUFFIX; + + private static final String DISPATCH_CLASSSHEET_FULLNAME = DISPATCH_CLASSSHEET_SPACE + "." + + CLASSSHEET_NAME; + + private static final String DISPATCH_CLASSTEMPLATE_SPACE = CLASS_SPACE_PREFIX + + com.xpn.xwiki.plugin.applicationmanager.core.doc.objects.classes.XClassManager.XWIKI_CLASSTEMPLATE_SPACE_SUFFIX; + + private static final String DISPATCH_CLASSTEMPLATE_FULLNAME = DISPATCH_CLASSTEMPLATE_SPACE + "." + + CLASSTEMPLATE_NAME; + + private static final String NODISPATCH_CLASS_SPACE = CLASS_SPACE_PREFIX; + + private static final String NODISPATCH_CLASS_FULLNAME = NODISPATCH_CLASS_SPACE + "." + CLASS_NAME; + + private static final String NODISPATCH_CLASSSHEET_SPACE = CLASS_SPACE_PREFIX; + + private static final String NODISPATCH_CLASSSHEET_FULLNAME = NODISPATCH_CLASSSHEET_SPACE + "." + + CLASSSHEET_NAME; + + private static final String NODISPATCH_CLASSTEMPLATE_SPACE = CLASS_SPACE_PREFIX; + + private static final String NODISPATCH_CLASSTEMPLATE_FULLNAME = NODISPATCH_CLASSTEMPLATE_SPACE + + "." + CLASSTEMPLATE_NAME; + + private static final String DEFAULT_ITEM_NAME = "item"; + + private static final String DEFAULT_ITEMDOCUMENT_NAME = CLASS_PREFIX + "Item"; + + private static final String DISPATCH_DEFAULT_ITEMDOCUMENT_FULLNAME = CLASS_SPACE_PREFIX + "." + + DEFAULT_ITEMDOCUMENT_NAME; + + private static final String NODISPATCH_DEFAULT_ITEMDOCUMENT_FULLNAME = CLASS_SPACE_PREFIX + "." + + DEFAULT_ITEMDOCUMENT_NAME; + + /** + * Name of field string. + */ + public static final String FIELD_string = "string"; + + /** + * Pretty name of field string. + */ + public static final String FIELDPN_string = "String"; + + /** + * Name of field string2. + */ + public static final String FIELD_string2 = "string2"; + + /** + * Pretty name of field string2. + */ + public static final String FIELDPN_string2 = "String2"; + + /** + * Name of field int. + */ + public static final String FIELD_int = "int"; + + /** + * Pretty name of field int. + */ + public static final String FIELDPN_int = "Int"; + + /** + * Name of field stringlist. + */ + public static final String FIELD_stringlist = "stringlist"; + + /** + * Pretty name of field stringlist. + */ + public static final String FIELDPN_stringlist = "String List"; + + /** + * Result of {@link AbstractXClassManager#createWhereClause(Object[][], List)} with no parameters. + */ + public static final String WHERECLAUSE_null = ", BaseObject as obj where doc.fullName=obj.name and obj.className=? and doc.fullName<>?"; + + public static final String[] WHERECLAUSE_doc0 = { "df0", null, "dv0" }; + + public static final String[] WHERECLAUSE_doc1 = { "df1", null, "dv1" }; + + public static final String[] WHERECLAUSE_obj0 = { "of0", "String", "ov0" }; + + public static final String[] WHERECLAUSE_obj1 = { "of1", "Int", "ov1" }; + + /** + * Result of {@link AbstractXClassManager#createWhereClause(Object[][], List)} with doc filter. + */ + public static final String WHERECLAUSE_doc = ", BaseObject as obj where doc.fullName=obj.name and obj.className=? and doc.fullName<>? and lower(doc.df0)=?"; + + public static final String[][] WHERECLAUSE_PARAM_doc = { WHERECLAUSE_doc0 }; + + /** + * Result of {@link AbstractXClassManager#createWhereClause(Object[][], List)} with more than one + * docs filters. + */ + public static final String WHERECLAUSE_doc_multi = ", BaseObject as obj where doc.fullName=obj.name and obj.className=? and doc.fullName<>? and lower(doc.df0)=? and lower(doc.df1)=?"; + + public static final String[][] WHERECLAUSE_PARAM_doc_multi = { WHERECLAUSE_doc0, + WHERECLAUSE_doc1 }; + + /** + * Result of {@link AbstractXClassManager#createWhereClause(Object[][], List)} with object filter. + */ + public static final String WHERECLAUSE_obj = ", BaseObject as obj, String as field0 where doc.fullName=obj.name and obj.className=? and doc.fullName<>? and obj.id=field0.id.id and field0.name=? and lower(field0.value)=?"; + + public static final String[][] WHERECLAUSE_PARAM_obj = { WHERECLAUSE_obj0 }; + + /** + * Result of {@link AbstractXClassManager#createWhereClause(Object[][], List)} with more than one + * objects filters. + */ + public static final String WHERECLAUSE_obj_multi = ", BaseObject as obj, String as field0, Int as field1 where doc.fullName=obj.name and obj.className=? and doc.fullName<>? and obj.id=field0.id.id and field0.name=? and lower(field0.value)=? and obj.id=field1.id.id and field1.name=? and lower(field1.value)=?"; + + public static final String[][] WHERECLAUSE_PARAM_obj_multi = { WHERECLAUSE_obj0, + WHERECLAUSE_obj1 }; + + /** + * Result of {@link AbstractXClassManager#createWhereClause(Object[][], List)} with object and doc + * filter. + */ + public static final String WHERECLAUSE_objdoc = ", BaseObject as obj, String as field1 where doc.fullName=obj.name and obj.className=? and doc.fullName<>? and lower(doc.df0)=? and obj.id=field1.id.id and field1.name=? and lower(field1.value)=?"; + + public static final String[][] WHERECLAUSE_PARAM_objdoc = { WHERECLAUSE_doc0, WHERECLAUSE_obj0 }; + + /** + * Result of {@link AbstractXClassManager#createWhereClause(Object[][], List)} with more than one + * objects and docs + * filters. + */ + public static final String WHERECLAUSE_objdoc_multi = ", BaseObject as obj, String as field2, Int as field3 where doc.fullName=obj.name and obj.className=? and doc.fullName<>? and lower(doc.df0)=? and lower(doc.df1)=? and obj.id=field2.id.id and field2.name=? and lower(field2.value)=? and obj.id=field3.id.id and field3.name=? and lower(field3.value)=?"; + + public static final String[][] WHERECLAUSE_PARAM_objdoc_multi = { WHERECLAUSE_doc0, + WHERECLAUSE_doc1, WHERECLAUSE_obj0, WHERECLAUSE_obj1 }; + + static abstract public class TestXClassManager extends AbstractXClassManager { + + /** + * Default constructor for XWikiApplicationClass. + */ + protected TestXClassManager(String spaceprefix, String prefix, boolean dispatch, + IModelAccessFacade modelAccess, ModelContext mContext, ModelUtils modelUtils) { + super(spaceprefix, prefix, dispatch, modelAccess, mContext, modelUtils); + } + + @Override + protected boolean updateBaseClass(BaseClass baseClass) { + boolean needsUpdate = super.updateBaseClass(baseClass); + + needsUpdate |= baseClass.addTextField(FIELD_string, FIELDPN_string, 30); + needsUpdate |= baseClass.addTextField(FIELD_string2, FIELDPN_string2, 30); + needsUpdate |= baseClass.addNumberField(FIELD_int, FIELDPN_int, 10, "integer"); + needsUpdate |= baseClass.addTextField(FIELD_stringlist, FIELDPN_stringlist, 80); + + return needsUpdate; + } + } + + static public class DispatchXClassManager extends TestXClassManager { + + /** + * Unique instance of XWikiApplicationClass; + */ + private static DispatchXClassManager instance = null; + + /** + * Return unique instance of XWikiApplicationClass and update documents for this context. + * + * @return XWikiApplicationClass Instance of XWikiApplicationClass. + * @throws XWikiException + */ + public static DispatchXClassManager getInstance(IModelAccessFacade modelAccess, + ModelContext mContext, ModelUtils modelUtils) throws XWikiException { + // if (instance == null) + instance = new DispatchXClassManager(modelAccess, mContext, modelUtils); + instance.check(mContext.getXWikiContext()); + return instance; + } + + /** + * Default constructor for XWikiApplicationClass. + */ + private DispatchXClassManager(IModelAccessFacade modelAccess, ModelContext mContext, + ModelUtils modelUtils) { + super(CLASS_SPACE_PREFIX, CLASS_PREFIX, true, modelAccess, mContext, modelUtils); + } + } + + static public class NoDispatchXClassManager extends TestXClassManager { + + /** + * Unique instance of XWikiApplicationClass; + */ + private static NoDispatchXClassManager instance = null; + + /** + * Return unique instance of XWikiApplicationClass and update documents for this context. + * + * @return XWikiApplicationClass Instance of XWikiApplicationClass. + * @throws XWikiException + */ + public static NoDispatchXClassManager getInstance(IModelAccessFacade modelAccess, + ModelContext mContext, ModelUtils modelUtils) throws XWikiException { + // if (instance == null) + instance = new NoDispatchXClassManager(modelAccess, mContext, modelUtils); + instance.check(mContext.getXWikiContext()); + return instance; + } + + /** + * Default constructor for XWikiApplicationClass. + */ + private NoDispatchXClassManager(IModelAccessFacade modelAccess, ModelContext mContext, + ModelUtils modelUtils) { + super(CLASS_SPACE_PREFIX, CLASS_PREFIX, false, modelAccess, mContext, modelUtils); + } + } + + @Test + public void testInitXClassManagerDispatch() throws XWikiException { + documents.clear(); + replayDefault(); + XClassManager xClassManager = DispatchXClassManager.getInstance( + modelAccessMock, mContext, modelUtils); + verifyDefault(); + assertEquals(CLASS_SPACE_PREFIX, xClassManager.getClassSpacePrefix()); + assertEquals(CLASS_PREFIX, xClassManager.getClassPrefix()); + + assertEquals(CLASS_NAME, xClassManager.getClassName()); + assertEquals(CLASSSHEET_NAME, xClassManager.getClassSheetName()); + assertEquals(CLASSTEMPLATE_NAME, xClassManager.getClassTemplateName()); + + assertEquals(DISPATCH_CLASS_SPACE, xClassManager.getClassSpace()); + assertEquals(DISPATCH_CLASS_FULLNAME, xClassManager.getClassFullName()); + assertEquals(DISPATCH_CLASSSHEET_SPACE, xClassManager.getClassSheetSpace()); + assertEquals(DISPATCH_CLASSSHEET_FULLNAME, xClassManager.getClassSheetFullName()); + assertEquals(DISPATCH_CLASSTEMPLATE_SPACE, xClassManager.getClassTemplateSpace()); + assertEquals(DISPATCH_CLASSTEMPLATE_FULLNAME, xClassManager.getClassTemplateFullName()); + } + + @Test + public void testInitXClassManagerNoDispatch() throws XWikiException { + documents.clear(); + replayDefault(); + XClassManager xClassManager = NoDispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils); + verifyDefault(); + assertEquals(CLASS_SPACE_PREFIX, xClassManager.getClassSpacePrefix()); + assertEquals(CLASS_PREFIX, xClassManager.getClassPrefix()); + + assertEquals(CLASS_NAME, xClassManager.getClassName()); + assertEquals(CLASSSHEET_NAME, xClassManager.getClassSheetName()); + assertEquals(CLASSTEMPLATE_NAME, xClassManager.getClassTemplateName()); + + assertEquals(NODISPATCH_CLASS_SPACE, xClassManager.getClassSpace()); + assertEquals(NODISPATCH_CLASS_FULLNAME, xClassManager.getClassFullName()); + assertEquals(NODISPATCH_CLASSSHEET_SPACE, xClassManager.getClassSheetSpace()); + assertEquals(NODISPATCH_CLASSSHEET_FULLNAME, xClassManager.getClassSheetFullName()); + assertEquals(NODISPATCH_CLASSTEMPLATE_SPACE, xClassManager.getClassTemplateSpace()); + assertEquals(NODISPATCH_CLASSTEMPLATE_FULLNAME, xClassManager.getClassTemplateFullName()); + } + + private void ptestCkeck(XClassManager xclass) throws XWikiException { + XWikiDocument doc = modelAccessMock.getOrCreateDocument(xclass.getClassDocRef()); + + assertFalse(doc.isNew()); + + BaseClass baseclass = doc.getxWikiClass(); + assertEquals(xclass.getClassFullName(), baseclass.getName()); + + PropertyInterface prop = baseclass.getField(FIELD_string); + assertNotNull(prop); + + prop = baseclass.getField(FIELD_stringlist); + assertNotNull(prop); + + // /// + + XWikiDocument docSheet = modelAccessMock.getOrCreateDocument(xclass.getClassSheetDocRef()); + assertFalse(docSheet.isNew()); + + // /// + + XWikiDocument docTemplate = modelAccessMock + .getOrCreateDocument(xclass.getClassTemplateDocRef()); + assertFalse(docTemplate.isNew()); + + BaseObject baseobject = docTemplate.getObject(xclass.getClassFullName()); + assertNotNull(baseobject); + } + + @Test + public void testCkeckDispatch() throws XWikiException { + documents.clear(); + replayDefault(); + ptestCkeck( + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils)); + verifyDefault(); + } + + @Test + public void testCkeckNoDispatch() throws XWikiException { + documents.clear(); + replayDefault(); + ptestCkeck( + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils)); + verifyDefault(); + } + + private void ptestGetClassDocument(XClassManager xClassManager) + throws XWikiException { + XWikiDocument doc = modelAccessMock.getOrCreateDocument(xClassManager.getClassDocRef()); + Document docFromClass = xClassManager.getClassDocument(getXContext()); + + assertFalse(docFromClass.isNew()); + assertEquals(doc.getFullName(), docFromClass.getFullName()); + } + + @Test + public void testGetClassDocumentDispatch() throws XWikiException { + documents.clear(); + replayDefault(); + ptestGetClassDocument( + DispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils)); + verifyDefault(); + } + + @Test + public void testGetClassDocumentNoDispatch() throws XWikiException { + documents.clear(); + replayDefault(); + ptestGetClassDocument( + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils)); + verifyDefault(); + } + + private void ptestGetClassSheetDocument(XClassManager xClassManager) + throws XWikiException { + XWikiDocument doc = modelAccessMock.getOrCreateDocument(xClassManager.getClassSheetDocRef()); + Document docFromClass = xClassManager.getClassSheetDocument(getXContext()); + verifyDefault(); + assertFalse(docFromClass.isNew()); + assertEquals(doc.getFullName(), docFromClass.getFullName()); + } + + @Test + public void testGetClassSheetDocumentDispatch() throws XWikiException { + documents.clear(); + replayDefault(); + ptestGetClassSheetDocument( + DispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils)); + verifyDefault(); + } + + @Test + public void testGetClassSheetDocumentNoDispatch() throws XWikiException { + documents.clear(); + replayDefault(); + ptestGetClassSheetDocument( + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils)); + verifyDefault(); + } + + private void ptestGetClassTemplateDocument(XClassManager xClassManager) + throws XWikiException { + XWikiDocument doc = modelAccessMock.getOrCreateDocument(xClassManager.getClassTemplateDocRef()); + Document docFromClass = xClassManager.getClassTemplateDocument(getXContext()); + verifyDefault(); + assertFalse(docFromClass.isNew()); + assertEquals(doc.getFullName(), docFromClass.getFullName()); + } + + @Test + public void testGetClassTemplateDocumentDispatch() throws Exception { + documents.clear(); + replayDefault(); + ptestGetClassTemplateDocument( + DispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils)); + verifyDefault(); + } + + @Test + public void testGetClassTemplateDocumentNoDispatch() throws Exception { + documents.clear(); + replayDefault(); + ptestGetClassTemplateDocument( + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils)); + verifyDefault(); + } + + @Test + public void testGetItemDefaultNameDisptach() throws XWikiException { + replayDefault(); + assertEquals(DEFAULT_ITEM_NAME, + DispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .getItemDefaultName( + DISPATCH_DEFAULT_ITEMDOCUMENT_FULLNAME)); + verifyDefault(); + } + + @Test + public void testGetItemDefaultNameNoDispatch() throws XWikiException { + replayDefault(); + assertEquals(DEFAULT_ITEM_NAME, + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .getItemDefaultName( + NODISPATCH_DEFAULT_ITEMDOCUMENT_FULLNAME)); + verifyDefault(); + } + + @Test + public void testGetItemDocumentDefaultNameDispatch() throws XWikiException { + replayDefault(); + assertEquals(DEFAULT_ITEMDOCUMENT_NAME, + DispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .getItemDocumentDefaultName(DEFAULT_ITEM_NAME, getXContext())); + verifyDefault(); + } + + @Test + public void testGetItemDocumentDefaultNameNoDispatch() throws XWikiException { + replayDefault(); + assertEquals(DEFAULT_ITEMDOCUMENT_NAME, + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .getItemDocumentDefaultName( + DEFAULT_ITEM_NAME, + getXContext())); + verifyDefault(); + } + + @Test + public void testGetItemDocumentDefaultFullNameDispatch() throws XWikiException { + replayDefault(); + assertEquals(DISPATCH_DEFAULT_ITEMDOCUMENT_FULLNAME, + DispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .getItemDocumentDefaultFullName( + DEFAULT_ITEM_NAME, + getXContext())); + verifyDefault(); + } + + @Test + public void testGetItemDocumentDefaultFullNameNoDispatch() throws XWikiException { + replayDefault(); + assertEquals(NODISPATCH_DEFAULT_ITEMDOCUMENT_FULLNAME, + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .getItemDocumentDefaultFullName( + DEFAULT_ITEM_NAME, + getXContext())); + verifyDefault(); + } + + @Test + public void testIsInstanceNoDispatch() throws XWikiException { + replayDefault(); + assertTrue(NoDispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils).isInstance( + NoDispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .newXObjectDocument(getXContext()) + .getDocumentApi())); + assertFalse( + NoDispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .isInstance(new XWikiDocument())); + verifyDefault(); + } + + @Test + public void testIsInstanceDispatch() throws XWikiException { + replayDefault(); + assertTrue(DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils).isInstance( + DispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .newXObjectDocument(getXContext()) + .getDocumentApi())); + assertFalse( + DispatchXClassManager.getInstance(modelAccessMock, mContext, modelUtils) + .isInstance(new XWikiDocument())); + verifyDefault(); + } + + @Test + public void testCreateWhereClause_null() throws XWikiException { + List list = new ArrayList<>(); + replayDefault(); + String where = DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .createWhereClause(null, list); + assertEquals(WHERECLAUSE_null, where); + verifyDefault(); + } + + @Test + public void testCreateWhereClause_nothing() throws XWikiException { + List list = new ArrayList<>(); + String[][] fieldDescriptors = new String[][] {}; + replayDefault(); + String where = DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .createWhereClause(fieldDescriptors, list); + assertEquals(WHERECLAUSE_null, where); + verifyDefault(); + } + + @Test + public void testCreateWhereClause_doc() throws XWikiException { + List list = new ArrayList<>(); + replayDefault(); + String where = DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .createWhereClause(WHERECLAUSE_PARAM_doc, list); + assertEquals(WHERECLAUSE_doc, where); + verifyDefault(); + } + + @Test + public void testCreateWhereClause_doc_multi() throws XWikiException { + List list = new ArrayList<>(); + replayDefault(); + String where = DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .createWhereClause(WHERECLAUSE_PARAM_doc_multi, list); + assertEquals(WHERECLAUSE_doc_multi, where); + verifyDefault(); + } + + @Test + public void testCreateWhereClause_obj() throws XWikiException { + List list = new ArrayList<>(); + replayDefault(); + String where = DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .createWhereClause(WHERECLAUSE_PARAM_obj, list); + assertEquals(WHERECLAUSE_obj, where); + verifyDefault(); + } + + @Test + public void testCreateWhereClause_obj_multi() throws XWikiException { + List list = new ArrayList<>(); + replayDefault(); + String where = DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .createWhereClause(WHERECLAUSE_PARAM_obj_multi, list); + assertEquals(WHERECLAUSE_obj_multi, where); + verifyDefault(); + } + + @Test + public void testCreateWhereClause_objdoc() throws XWikiException { + List list = new ArrayList<>(); + replayDefault(); + String where = DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .createWhereClause(WHERECLAUSE_PARAM_objdoc, list); + assertEquals(WHERECLAUSE_objdoc, where); + verifyDefault(); + } + + @Test + public void testCreateWhereClause_objdoc_multi() throws XWikiException { + List list = new ArrayList<>(); + replayDefault(); + String where = DispatchXClassManager + .getInstance(modelAccessMock, mContext, modelUtils) + .createWhereClause(WHERECLAUSE_PARAM_objdoc_multi, list); + assertEquals(WHERECLAUSE_objdoc_multi, where); + verifyDefault(); + } + + /******************* + * HELPER METHODS + *******************/ + + private @NotNull XWikiDocument stubGetDocument(DocumentReference theDocRef) + throws DocumentNotExistsException { + if (documents.containsKey(theDocRef)) { + return documents.get(theDocRef); + } else { + throw new DocumentNotExistsException(theDocRef); + } + } + + private @NotNull XWikiDocument stubGetOrCreateDocument(DocumentReference theDocRef) + throws DocumentNotExistsException { + if (documents.containsKey(theDocRef)) { + return documents.get(theDocRef); + } else { + XWikiDocument doc = new XWikiDocument(theDocRef); + doc.setNew(true); + doc.setLanguage(DEFAULT_LANG); + doc.setDefaultLanguage(DEFAULT_LANG); + doc.setTranslation(0); + Date creationDate = new Date(); + doc.setCreationDate(creationDate); + doc.setContentUpdateDate(creationDate); + doc.setDate(creationDate); + doc.setCreator(XWikiRightService.SUPERADMIN_USER); + doc.setAuthor(XWikiRightService.SUPERADMIN_USER); + doc.setContentAuthor(XWikiRightService.SUPERADMIN_USER); + doc.setContent(""); + doc.setContentDirty(true); + doc.setMetaDataDirty(true); + doc.setOriginalDocument(new XWikiDocument(doc.getDocumentReference())); + doc.setSyntax(Syntax.XWIKI_1_0); + return doc; + } + } + + private void mockGetDocAndSaveDoc() throws Exception { + expect(modelAccessMock.getDocument(isA(DocumentReference.class))) + .andAnswer(() -> stubGetDocument(getCurrentArgument(0))).anyTimes(); + expect(modelAccessMock.getOrCreateDocument(isA(DocumentReference.class))) + .andAnswer(() -> stubGetOrCreateDocument(getCurrentArgument(0))).anyTimes(); + IAnswer saveDocStub = () -> { + XWikiDocument theDoc = getCurrentArgument(0); + theDoc.setNew(false); + documents.put(theDoc.getDocumentReference(), theDoc); + return null; + }; + modelAccessMock.saveDocument(isA(XWikiDocument.class)); + expectLastCall().andAnswer(saveDocStub).anyTimes(); + modelAccessMock.saveDocument(isA(XWikiDocument.class), isA(String.class)); + expectLastCall().andAnswer(saveDocStub).anyTimes(); + modelAccessMock.saveDocument(isA(XWikiDocument.class), isA(String.class), anyBoolean()); + expectLastCall().andAnswer(saveDocStub).anyTimes(); + } + + private void mockClearName() throws Exception { + expect(xwiki.clearName(isA(String.class), same(getXContext()))) + .andAnswer(() -> { + return getCurrentArgument(0); + }).anyTimes(); + expect(xwiki.clearName(isA(String.class), anyBoolean(), anyBoolean(), same(getXContext()))) + .andAnswer(() -> { + return getCurrentArgument(0); + }).anyTimes(); + } + + private void mockGetXClass() throws Exception { + expect(xwiki.getXClass(isA(DocumentReference.class), same(getXContext()))) + .andAnswer(() -> { + DocumentReference classReference = getCurrentArgument(0); + XWikiDocument doc = stubGetDocument(classReference); + return doc.getxWikiClass(); + }).anyTimes(); + } + +} diff --git a/celements-xapp/component/target-eclipse/classes/applicationmanager/ApplicationResources.properties b/celements-xapp/component/target-eclipse/classes/applicationmanager/ApplicationResources.properties new file mode 100644 index 000000000..8188a79a3 --- /dev/null +++ b/celements-xapp/component/target-eclipse/classes/applicationmanager/ApplicationResources.properties @@ -0,0 +1,27 @@ +# comments +applicationmanager.plugin.comment.createapplication=Create application [{0}] +applicationmanager.plugin.comment.importapplication=Import application [{0}] +applicationmanager.plugin.comment.reloadapplication=Reload application [{0}] +applicationmanager.plugin.comment.reloadallapplications=Reload all applications +applicationmanager.plugin.comment.autoupdatetranslations=Automatically update translations informations from applications in [{0}] +applicationmanager.plugin.comment.refreshalltranslations=Refresh all applications translations informations + +# messages +applicationmanager.plugin.error.applicationpagealreadyexists=Application [{0}] descriptor page already exists +applicationmanager.plugin.error.import.packagedoesnotexists=Package [{0}] does not exists +applicationmanager.plugin.error.import.import=Fail to import package [{0}] +applicationmanager.plugin.error.import.install=Fail to install package [{0}] +applicationmanager.plugin.error.applicationdoesnotexists=Application [{0}] does not exists + +# log +applicationmanager.plugin.log.createapplication=Application creation failed [{0}] +applicationmanager.plugin.log.deleteapplication=Application [{0}] failed +applicationmanager.plugin.log.getallapplications=Get all applications descriptors failed +applicationmanager.plugin.log.getapplication=Get application descriptor failed +applicationmanager.plugin.log.exportapplication=Application [{0}] export as a XAR package failed +applicationmanager.plugin.log.importapplication=Application import from XAR package [{0}] failed +applicationmanager.plugin.log.reloadapplication=Application [{0}] reload failed +applicationmanager.plugin.log.realoadallapplications=All applications reload failed +applicationmanager.plugin.log.getrootapplication=Get wiki root application failed +applicationmanager.plugin.log.autoupdatetranslations=Error when automatically updating translations informations from applications in [{0}] +applicationmanager.plugin.log.refreshalltranslations=Refresh all applications translations informations \ No newline at end of file diff --git a/celements-xapp/component/target-eclipse/classes/sheets/XAppSheets.XWikiApplicationClassSheet.vm b/celements-xapp/component/target-eclipse/classes/sheets/XAppSheets.XWikiApplicationClassSheet.vm new file mode 100644 index 000000000..cf340b355 --- /dev/null +++ b/celements-xapp/component/target-eclipse/classes/sheets/XAppSheets.XWikiApplicationClassSheet.vm @@ -0,0 +1,61 @@ +#set($Appmanager = $xwiki.applicationmanager) +#set($Exception = $Appmanager.DefaultException) +## +#set($object = $doc.getObject("XAppClasses.XWikiApplicationClass")) +## +#if($object) + #set($class = $object.xWikiClass) + #set($appname = $doc.getValue("appname", $object)) +#end +## +############################################################################# +## +#if($appname && $appname.length() > 0) + ## + #set($app = $Appmanager.getApplicationDocument($doc.getValue("appname", $object))) + ## + #if($context.action == "view") + 1 Application \"#if($app.appprettyname && $app.appprettyname.trim() != "")$app.appprettyname#else$app.appname#end\" + #end + ## +
+ #foreach($prop in $class.properties) +
${prop.prettyName}
+ #if($context.action == "view" && $prop.name == "documents") +
+ #foreach($docfullname in $app.getDocumentsNames(false, false)) + [$docfullname>$docfullname] + #end +
+ #elseif($context.action == "view" && $prop.name == "docstoinclude") +
+ #foreach($docfullname in $app.getDocsNameToInclude(false)) + [$docfullname>$docfullname] + #end +
+ #elseif($context.action == "view" && $prop.name == "docstolink") +
+ #foreach($docfullname in $app.getDocsNameToLink(false)) + [$docfullname>$docfullname] + #end +
+ #elseif($context.action == "view" && $prop.name == "translationdocs") +
+ #foreach($docfullname in $app.getTranslationDocs()) + [$docfullname>$docfullname] + #end +
+ #elseif($context.action == "view" && $prop.name == "applications") +
+ #foreach($appname in $app.getApplications()) + [$appname>$Appmanager.getApplicationDocument($appname).fullName] + #end +
+ #else +
$doc.display($prop.getName())
+ #end + #end +
+#else + 1 Document \"$doc.name\" +#end \ No newline at end of file diff --git a/celements-xapp/web-module/pom.xml b/celements-xapp/web-module/pom.xml new file mode 100644 index 000000000..c4f3fc052 --- /dev/null +++ b/celements-xapp/web-module/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + com.celements + celementsweb + 6.0-SNAPSHOT + + celements-xapp-web + 6.0-SNAPSHOT + Celements XApp Web-Module + war + + + com.celements + celements-xapp + 6.0-SNAPSHOT + + + + celements-xapp-web + + + com.jarslab.maven + babel-maven-plugin + + + + + scm:git:git@github.com:celements/celements-features.git + scm:git:git@github.com:celements/celements-features.git + https://github.com/celements/celements-features/celements-xapp/web-module + HEAD + + diff --git a/celements-xapp/web-module/src/main/resources/XApp/XWikiApplicationXapp b/celements-xapp/web-module/src/main/resources/XApp/XWikiApplicationXapp new file mode 100644 index 000000000..93ce4877b --- /dev/null +++ b/celements-xapp/web-module/src/main/resources/XApp/XWikiApplicationXapp @@ -0,0 +1,274 @@ + + + +XApp +XWikiApplicationXapp + +en +0 +XAppClasses.XWikiApplicationClass + +XWiki.Admin + +XWiki.Admin +1188210392000 +1188464736000 +1189614079000 +1.1 + + + + + + + +XAppClasses.XWikiApplicationClass + + + + + + + + +0 + +input +1 +applications +5 + +Applications +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + + +appname +1 +Application Name +30 + +0 + + +com.xpn.xwiki.objects.classes.StringClass + + + +appversion +3 +Application Version +30 + +0 + + +com.xpn.xwiki.objects.classes.StringClass + + +0 + +input +1 +dependencies +4 + +Dependencies +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +FullyRenderedText + +--- +description +2 +Description +5 +40 + +0 + + +com.xpn.xwiki.objects.classes.TextAreaClass + + +0 + +input +1 +docstoinclude +7 + +Documents to include +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +docstolink +8 + +Documents to link +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +documents +6 + +Documents +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +translationdocs +9 + +Translations documents +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +XApp.XWikiApplicationXapp +0 +XAppClasses.XWikiApplicationClass + + +XWikiApplicationClass + + + +xapp + + +1.0 + + + +applicationmanager + + + + + + + + + + + + + +XAppManager.WebHome +XAppManager.CreateApplication +XAppResources.Translations + + + + +XAppResources.Translations + + + + + +XWiki.TagClass + + + + + + + + +0 +input +1 +tags +1 +Tags +1 + + ,| +30 +0 + +com.xpn.xwiki.objects.classes.StaticListClass + + +XApp.XWikiApplicationXapp +0 +XWiki.TagClass + + + + +#includeForm("XAppSheets.XWikiApplicationClassSheet") + \ No newline at end of file diff --git a/celements-xapp/web-module/src/main/resources/XApp/XWikiApplicationXwikiapplicationclass b/celements-xapp/web-module/src/main/resources/XApp/XWikiApplicationXwikiapplicationclass new file mode 100644 index 000000000..db2c7564e --- /dev/null +++ b/celements-xapp/web-module/src/main/resources/XApp/XWikiApplicationXwikiapplicationclass @@ -0,0 +1,268 @@ + + + +XApp +XWikiApplicationXwikiapplicationclass + +en +0 +XAppClasses.XWikiApplicationClass + +XWiki.Admin + +XWiki.Admin +1188210358000 +1188464753000 +1189614079000 +1.1 + + + + + + + +XAppClasses.XWikiApplicationClass + + + + + + + + +0 + +input +1 +applications +5 + +Applications +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + + +appname +1 +Application Name +30 + +0 + + +com.xpn.xwiki.objects.classes.StringClass + + + +appversion +3 +Application Version +30 + +0 + + +com.xpn.xwiki.objects.classes.StringClass + + +0 + +input +1 +dependencies +4 + +Dependencies +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +FullyRenderedText + +--- +description +2 +Description +5 +40 + +0 + + +com.xpn.xwiki.objects.classes.TextAreaClass + + +0 + +input +1 +docstoinclude +7 + +Documents to include +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +docstolink +8 + +Documents to link +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +documents +6 + +Documents +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +translationdocs +9 + +Translations documents +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +XApp.XWikiApplicationXwikiapplicationclass +0 +XAppClasses.XWikiApplicationClass + + + + +XWikiApplicationClass + + +1.0 + + + + + + + + + + + + + + + +XAppClasses.XWikiApplicationClass +XAppSheets.XWikiApplicationClassSheet +XAppTemplates.XWikiApplicationClassTemplate + + + + + + + + +XWiki.TagClass + + + + + + + + +0 +input +1 +tags +1 +Tags +1 + + ,| +30 +0 + +com.xpn.xwiki.objects.classes.StaticListClass + + +XApp.XWikiApplicationXwikiapplicationclass +0 +XWiki.TagClass + + + + +#includeForm("XAppSheets.XWikiApplicationClassSheet") + \ No newline at end of file diff --git a/celements-xapp/web-module/src/main/resources/XAppClasses/XWikiApplicationClass b/celements-xapp/web-module/src/main/resources/XAppClasses/XWikiApplicationClass new file mode 100644 index 000000000..e43fb1375 --- /dev/null +++ b/celements-xapp/web-module/src/main/resources/XAppClasses/XWikiApplicationClass @@ -0,0 +1,199 @@ + + + +XAppClasses +XWikiApplicationClass + +en +0 + + +XWiki.Admin + +XWiki.Admin +1188210015000 +1188466680000 +1189614079000 +1.1 + + + + + + +XAppClasses.XWikiApplicationClass + + + + + + + + +0 + +input +1 +applications +5 + +Applications +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + + +appname +1 +Application Name +30 + +0 + + +com.xpn.xwiki.objects.classes.StringClass + + + +appversion +3 +Application Version +30 + +0 + + +com.xpn.xwiki.objects.classes.StringClass + + +0 + +input +1 +dependencies +4 + +Dependencies +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +FullyRenderedText + +--- +description +2 +Description +5 +40 + +0 + + +com.xpn.xwiki.objects.classes.TextAreaClass + + +0 + +input +1 +docstoinclude +7 + +Documents to include +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +docstolink +8 + +Documents to link +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +documents +6 + +Documents +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +translationdocs +9 + +Translations documents +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + + + \ No newline at end of file diff --git a/celements-xapp/web-module/src/main/resources/XAppManager/CreateApplication b/celements-xapp/web-module/src/main/resources/XAppManager/CreateApplication new file mode 100644 index 000000000..73103f2ee --- /dev/null +++ b/celements-xapp/web-module/src/main/resources/XAppManager/CreateApplication @@ -0,0 +1,114 @@ + + + +XAppManager +CreateApplication + +en +0 +XAppManager.WebHome +XWiki.Admin +XWiki.Admin + +XWiki.Admin +1186500200000 +1188308778000 +1189614079000 +1.1 + + + + + + + +XWiki.TagClass + + + + + + + + +0 +input +1 +tags +1 +Tags +1 + + ,| +30 +0 + +com.xpn.xwiki.objects.classes.StaticListClass + + +XAppManager.CreateApplication +0 +XWiki.TagClass + + + + +#set($AppManager = $xwiki.applicationmanager) +#set($Exception = $AppManager.DefaultException) + +#set($appdocument = $AppManager.createApplicationDocument()) +#set($appdocumentobject = $appdocument.updateObjectFromRequest("XAppClasses.XWikiApplicationClass")) + +#set($action = $request.getParameter("action")) + +############################################################################# + +1 $msg.get("xapp.createapplication") + +#if ($action) + #if($action == "create") + #set($result = $AppManager.createApplication($appdocument, true)) + #if ($result == $Exception.ERROR_NOERROR) + <font color=green> + $msg.get("xapp.successcreateapp", [$appdocument.appname, $appdocument.getFullName()]) + </font> + #else + <font color=red> + #if ($result == $Exception.ERROR_APPLICATIONMANAGER_APPDOC_ALREADY_EXISTS) + $msg.get("xapp.errorcreateapp.ERROR_APPLICATIONMANAGER_APPDOC_ALREADY_EXISTS", [$appdocument.appname]) + #else + $msg.get("xapp.errorcreateapp", [$appdocument.appname, $result]) + #end + </font> + #end + #end +#end + +<style> + .xem_userlisttable td {} +</style> + +<form method="get" name="createapplication"> + <input type="hidden" name="action" value="create"> + + <table width="100%"> + <tr> + <td>$msg.get("xapp.appname")</td> + <td>$appdocument.display("appname", "edit")</td> + <td id="appnameerrormsg" width="40%"></td> + </tr> + <tr> + <td>$msg.get("xapp.description")</td> + <td>$appdocument.display("description", "edit")</td> + <td id="appdescerrormsg" width="40%"></td> + </tr> + <tr> + <td>$msg.get("version")</td> + <td>$appdocument.display("appversion", "edit")</td> + <td id="appversionerrormsg" width="40%"></td> + </tr> + </table> + + <input type="submit" value="$msg.get("create")"> +</form> + diff --git a/celements-xapp/web-module/src/main/resources/XAppManager/WebHome b/celements-xapp/web-module/src/main/resources/XAppManager/WebHome new file mode 100644 index 000000000..abe0cad84 --- /dev/null +++ b/celements-xapp/web-module/src/main/resources/XAppManager/WebHome @@ -0,0 +1,148 @@ + + + +XAppManager +WebHome + +en +1 + +XWiki.Admin +XWiki.Admin + +XWiki.Admin +1186499908000 +1189787342000 +1189787342000 +1.3 + + + + +Deletion of attachment xwiki-enterprise-wiki.xar + + +XWiki.TagClass + + + + + + + + +0 +input +1 +tags +1 +Tags +1 + + ,| +30 +0 + +com.xpn.xwiki.objects.classes.StaticListClass + + +XAppManager.WebHome +0 +XWiki.TagClass + + + + +#set($Appmanager = $xwiki.applicationmanager) +#set($Exception = $Appmanager.DefaultException) + +#set($action = $request.getParameter("action")) +#set($confirm = $request.getParameter("confirm")) + +#set($appname = $request.getParameter("appname")) +#set($packagename = $request.getParameter("packagename")) + +############################################################################# + +#if($action) + #if($action == "delete") + #if($confirm == 1) + #set($result = $Appmanager.deleteApplication($appname)) + #if ($result == $Exception.ERROR_NOERROR) + <font color=green> + $msg.get("xapp.successdeleteapp", [$appname]) + </font> + #else + <font color=red> + $msg.get("xapp.errordeleteapp", [$appname, $result]) + </font> + #end + #else + #xwikimessagebox($msg.get("xapp.answer.deleteapp.title") $msg.get("xapp.answer.deleteapp.msg", [$appname]) $doc.getURL("view", "confirm=1&action=delete&appname=$appname") $doc.getURL("view") $msg.get("yes") $msg.get("no")) + #end + #elseif($action == "export") + #set($result = $Appmanager.exportApplicationXAR($appname)) + #if ($result == $Exception.ERROR_NOERROR) + <font color=green> + $msg.get("xapp.successexportapp", [$appname]) + </font> + #else + <font color=red> + $msg.get("xapp.errorexportapp", [$appname, $result]) + </font> + #end + #elseif($action == "import") + #set($result = $Appmanager.importApplication($packagename)) + #if ($result == $Exception.ERROR_NOERROR) + <font color=green> + $msg.get("xapp.successimportapp", [$packagename]) + </font> + #else + <font color=red> + $msg.get("xapp.errorimportapp", [$packagename, $result]) + </font> + #end + #elseif($action == "reload") + #set($result = $Appmanager.reloadApplication($appname)) + #if ($result == $Exception.ERROR_NOERROR) + <font color=green> + $msg.get("xapp.successreloadapp", [$appname]) + </font> + #else + <font color=red> + $msg.get("xapp.errorreloadapp", [$appname, $result]) + </font> + #end + #end +#end + +############################################################################# + +1 $msg.get("xapp.appmanager") + +#set($listApp = $Appmanager.getApplicationDocumentList()) + +$msg.get("xapp.totalnumberofapp") : $listApp.size() + +[$msg.get("xapp.createapplication")>CreateApplication] + +#if($listApp.size() > 0) + {table} + $msg.get("xapp.appname") | $msg.get("version") | $msg.get("xapp.description") | $msg.get("more") + #foreach($app in $listApp) + [$app.appname>$app.getFullName()] | $app.appversion | $app.description | [$msg.get("delete")>?action=delete&appname=$app.appname] [$msg.get("export")>?action=export&appname=$app.appname] [$msg.get("xapp.action.reloadapp")>?action=reload&appname=$app.appname] <a href="$app.getURL("inline")">$msg.get("edit")</a> + #end + {table} +#end + +############################################################################# + +#if($context.hasAdminRights()) + 1.1 $msg.get("xapp.availablefilestoimport") + + #set($listpackage=$doc.getAttachmentList()) + #foreach ($package in $listpackage) + - [$package.getFilename()>?action=import&packagename=$package.getFilename()] + #end +#end + diff --git a/celements-xapp/web-module/src/main/resources/XAppResources/Translations b/celements-xapp/web-module/src/main/resources/XAppResources/Translations new file mode 100644 index 000000000..316eb8454 --- /dev/null +++ b/celements-xapp/web-module/src/main/resources/XAppResources/Translations @@ -0,0 +1,86 @@ + + + +XAppResources +Translations + +en +0 + +XWiki.Admin +XWiki.Admin + +XWiki.Admin +1186671402000 +1188311676000 +1189614079000 +1.1 + + + + + + + +XWiki.TagClass + + + + + + + + +0 +input +1 +tags +1 +Tags +1 + + ,| +30 +0 + +com.xpn.xwiki.objects.classes.StaticListClass + + +XAppResources.Translations +0 +XWiki.TagClass + + + + +xapp.appmanager=Applications manager +xapp.totalnumberofapp=Total number of applications +xapp.createapplication=Create new application + +## LABELS +xapp.appname=Application +xapp.description=Description +xapp.docname=Document Name +xapp.availablefilestoimport=Available files to import + +## ACTIONS + +xapp.action.reloadapp=Reload + +## ANSWER +xapp.answer.deleteapp.title=Delete application +xapp.answer.deleteapp.msg=Are you sure you wish to delete application \"{0}\" ? + +## MESSAGES +xapp.successexportapp=Export application \"{0}\" succes. +xapp.errorexportapp=An error occurred while exporting application \"{0}\" (code \"{1}\"). +xapp.successimportapp=Import application package \"{0}\" succes. +xapp.errorimportapp=An error occurred while importing application package \"{0}\" (code \"{1}\"). +xapp.successdeleteapp=Delete application \"{0}\" succes. +xapp.errordeleteapp=An error occurred while deleting application \"{0}\" (code \"{1}\"). +xapp.successcreateapp=Application \"{0}\" has been created. You can access it at [{1}]. +xapp.errorcreateapp=An error occurred while creating application \"{0}\" (code \"{1}\"). +xapp.errorcreateapp.ERROR_APPLICATIONMANAGER_APPDOC_ALREADY_EXISTS=Application \"{0}\" already exists. Choose another name. +xapp.successreloadapp=Reload application \"{0}\" succes. +xapp.errorreloadapp=An error occurred while reloading application \"{0}\" (code \"{1}\"). + diff --git a/celements-xapp/web-module/src/main/resources/XAppSheets/XWikiApplicationClassSheet b/celements-xapp/web-module/src/main/resources/XAppSheets/XWikiApplicationClassSheet new file mode 100644 index 000000000..01aae13a4 --- /dev/null +++ b/celements-xapp/web-module/src/main/resources/XAppSheets/XWikiApplicationClassSheet @@ -0,0 +1,35 @@ + + + +XAppSheets +XWikiApplicationClassSheet + + +0 + + +XWiki.Admin + + +1188210015000 +1188210015000 +1189614079000 +1.1 + + + + + +## you can modify this page to customize the presentation of your object + +1 Document $doc.name + +#set($class = $doc.getObject("XAppClasses.XWikiApplicationClass").xWikiClass) + +<dl> + #foreach($prop in $class.properties) + <dt> ${prop.prettyName} </dt> + <dd>$doc.display($prop.getName())</dd> + #end +</dl> + \ No newline at end of file diff --git a/celements-xapp/web-module/src/main/resources/XAppTemplates/XWikiApplicationClassTemplate b/celements-xapp/web-module/src/main/resources/XAppTemplates/XWikiApplicationClassTemplate new file mode 100644 index 000000000..2ea5cf7e3 --- /dev/null +++ b/celements-xapp/web-module/src/main/resources/XAppTemplates/XWikiApplicationClassTemplate @@ -0,0 +1,204 @@ + + + +XAppTemplates +XWikiApplicationClassTemplate + + +0 + + +XWiki.Admin + + +1188210015000 +1188210015000 +1189614079000 +1.1 + + + + + + + +XAppClasses.XWikiApplicationClass + + + + + + + + +0 + +input +1 +applications +5 + +Applications +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + + +appname +1 +Application Name +30 + +0 + + +com.xpn.xwiki.objects.classes.StringClass + + + +appversion +3 +Application Version +30 + +0 + + +com.xpn.xwiki.objects.classes.StringClass + + +0 + +input +1 +dependencies +4 + +Dependencies +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +FullyRenderedText + +--- +description +2 +Description +5 +40 + +0 + + +com.xpn.xwiki.objects.classes.TextAreaClass + + +0 + +input +1 +docstoinclude +7 + +Documents to include +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +docstolink +8 + +Documents to link +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +documents +6 + +Documents +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +0 + +input +1 +translationdocs +9 + +Translations documents +0 +| +| +80 +none + +0 + + + +com.xpn.xwiki.objects.classes.StaticListClass + + +XAppTemplates.XWikiApplicationClassTemplate +0 +XAppClasses.XWikiApplicationClass + +#includeForm("XAppSheets.XWikiApplicationClassSheet") + \ No newline at end of file diff --git a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java index 185bf4444..66432ef77 100644 --- a/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java +++ b/celements-xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java @@ -7328,7 +7328,12 @@ public boolean is10Syntax(String syntaxId) { private void init(DocumentReference reference) { // if the passed reference is null consider it points to the default reference if (reference == null) { - setDocumentReference(Utils.getComponent(DocumentReferenceResolver.class).resolve("")); + if (getXWikiContext() != null) { + setDocumentReference(Utils.getComponent(DocumentReferenceResolver.class).resolve("", + new WikiReference(getXWikiContext().getDatabase()))); + } else { + setDocumentReference(Utils.getComponent(DocumentReferenceResolver.class).resolve("")); + } } else { setDocumentReference(reference); }