Java tutorial
/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.curriki.xwiki.plugin.asset; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.util.*; import org.apache.bcel.classfile.Constant; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.collections.ListUtils; import org.apache.velocity.VelocityContext; import org.curriki.xwiki.plugin.asset.attachment.*; import org.curriki.xwiki.plugin.asset.composite.CollectionCompositeAsset; import org.curriki.xwiki.plugin.asset.composite.FolderCompositeAsset; import org.curriki.xwiki.plugin.asset.composite.RootCollectionCompositeAsset; import org.curriki.xwiki.plugin.asset.other.InvalidAsset; import org.curriki.xwiki.plugin.asset.other.ProtectedAsset; import org.curriki.xwiki.plugin.asset.text.TextAssetManager; import org.curriki.xwiki.plugin.asset.text.TextAsset; import org.curriki.xwiki.plugin.mimetype.MimeTypePlugin; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.plugin.fileupload.FileUploadPlugin; import com.xpn.xwiki.api.Document; import com.xpn.xwiki.api.Property; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.doc.XWikiLock; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.objects.BaseStringProperty; import com.xpn.xwiki.api.Object; import com.xpn.xwiki.api.Attachment; public class Asset extends CurrikiDocument { private static final Log LOG = LogFactory.getLog(Asset.class); public Asset(XWikiDocument doc, XWikiContext context) { super(doc, context); } /** * Provide a title valid to insert in a javascript call in a tag onclick * @return */ public String getJSTitle() { return Util.escapeForJS(getDisplayTitle()); } /** * Provide a title valid to insert in a javascript call in a tag onclick * with a title length limitation * @param length * @return */ public String getJSTitle(int length) { return Util.escapeForJS(getDisplayTitle(length)); } /** * Provide the document fullname escaped for inclusing in a js call in a tab onclick * @return */ public String getJSFullName() { return Util.escapeForJS(getFullName()); } /** * Provided a truncated title to the length specified * @param length * @return */ public String getDisplayTitle(int length) { String title = getDisplayTitle(); if (title.length() > length) { title = title.substring(0, length); title = title + "..."; } return title; } /** * Return a display call without the {pre} tags to be usable in plain vm templates * @param fieldname * @param mode * @param nopre * @return */ public String display(String fieldname, String mode, boolean nopre) { String result = super.display(fieldname, mode); if (nopre) { return result.replaceAll("\\{/?pre\\}", ""); } else return result; } public String getCategory() { if (hasA(Constants.ASSET_CLASS)) { Object obj = getObject(Constants.ASSET_CLASS); use(obj); String category = (String) getValue(Constants.ASSET_CLASS_CATEGORY); if (category == null) return Constants.ASSET_CATEGORY_UNKNOWN; else return category; } else { return Constants.ASSET_CATEGORY_UNKNOWN; } } public String getFiletype() { String className = getActiveClass(); try { use(Constants.ATTACHMENT_ASSET_CLASS); return (String) getValue(Constants.ATTACHMENT_ASSET_FILE_TYPE); } finally { if (className != null) use(className); } } public String getFiletypeClass() { String filetype = getFiletype(); if ((filetype == null) || ("".equals(filetype))) return "unknown"; else return filetype; } public String getCategoryClass() { String className = getActiveClass(); String category = getCategory(); if (Constants.ASSET_CATEGORY_COLLECTION.equals(category)) { try { use(Constants.COMPOSITE_ASSET_CLASS); String colltype = (String) getValue(Constants.COMPOSITE_ASSET_CLASS_TYPE); // in case it is a folder we want folder and not collection if (Constants.COMPOSITE_ASSET_CLASS_TYPE_SUBFOLDER.equals(colltype)) { return "folder"; } } finally { if (className != null) use(className); } } if (Constants.ASSET_CATEGORY_ATTACHMENT.equals(category)) { // it the attachment is unknown or empty we want "attachment-unknown" and not "attachment" if ("unknown".equals(getFiletypeClass())) return "attachment-unknown"; } if ((category == null) || ("".equals(category))) return "unknown"; else return category; } public void setCategory(String category) { BaseObject obj = getDoc().getObject(Constants.ASSET_CLASS, true, context); obj.setStringValue(Constants.ASSET_CLASS_CATEGORY, category); } /** * Gets the category sub-type (file type in the case of attachments) * * This should be overridden in each of the sub-classes of Asset * * @return category sub-type */ public String getCategorySubtype() { Asset asset; try { asset = subclassAs(getAssetClass()); // If we didn't get a subtype then return unknown subtype if (asset.getClass().getSimpleName().equals("Asset")) { return Constants.ASSET_CATEGORY_SUBTYPE_UNKNOWN; } return asset.getCategorySubtype(); } catch (XWikiException e) { return Constants.ASSET_CATEGORY_SUBTYPE_UNKNOWN; } } /** * This functions will display the asset including a fallback system * For a specific mode. This function can be overidden for a specific asset type * Otherwise it will use a default rule system to find the appropriate template * @return */ public String displayAsset(String mode) { String result = ""; Asset asset = null; // we should subclass the asset to have access // to more functions try { asset = subclassAs(getAssetClass()); } catch (XWikiException e) { asset = this; } java.lang.Object previousAsset = null; VelocityContext vcontext = null; try { vcontext = (VelocityContext) context.get("vcontext"); previousAsset = vcontext.get("asset"); vcontext.put("asset", asset); // run the displayer result = asset.displayAssetTemplate(mode); } finally { if (vcontext != null) { if (previousAsset == null) vcontext.remove("asset"); else vcontext.put("asset", previousAsset); } } return result; } /** * This functions will display the asset including a fallback system * For a specific mode. This function can be overidden for a specific asset type * Otherwise it will use a default rule system to find the appropriate template * @return */ protected String displayAssetTemplate(String mode) { MimeTypePlugin mimePlugin = getMimeTypePlugin(); String category = getCategory(); String displayer = mimePlugin.getDisplayer(category, null, context); String result = context.getWiki().parseTemplate("assets/displayers/" + displayer + "_" + mode + ".vm", context); if (result.equals("")) result = context.getWiki().parseTemplate("assets/displayers/" + category + "_" + mode + ".vm", context); if (result.equals("")) result = context.getWiki().parseTemplate("assets/displayers/" + mode + ".vm", context); return result; } public void saveDocument(String comment) throws XWikiException { saveDocument(comment, false); } public static Asset createTempAsset(String parentAsset, XWikiContext context) throws XWikiException { return createTempAsset(parentAsset, null, context); } public static Asset createTempAsset(String parentAsset, String publishSpace, XWikiContext context) throws XWikiException { if (Constants.GUEST_USER.equals(context.getUser())) { throw new AssetException(AssetException.ERROR_ASSET_FORBIDDEN, "XWikiGuest cannot create assets."); } String pageName = context.getWiki().getUniquePageName(Constants.ASSET_TEMPORARY_SPACE, context); XWikiDocument newDoc = context.getWiki().getDocument(Constants.ASSET_TEMPORARY_SPACE, pageName, context); Asset assetDoc = new Asset(newDoc, context); assetDoc.init(parentAsset, publishSpace); assetDoc.saveDocument(context.getMessageTool().get("curriki.comment.createnewsourceasset"), true); return assetDoc; } public static Asset copyTempAsset(String copyOf, XWikiContext context) throws XWikiException { return copyTempAsset(copyOf, null, context); } public static Asset copyTempAsset(String copyOf, String publishSpace, XWikiContext context) throws XWikiException { if (Constants.GUEST_USER.equals(context.getUser())) { throw new AssetException(AssetException.ERROR_ASSET_FORBIDDEN, "XWikiGuest cannot create assets."); } Asset copyDoc = fetchAsset(copyOf, context); if (!copyDoc.assertCanDuplicate()) { throw new AssetException(AssetException.ERROR_ASSET_FORBIDDEN, "Source resource cannot be copied"); } String pageName = context.getWiki().getUniquePageName(Constants.ASSET_TEMPORARY_SPACE, context); XWikiDocument newDoc = copyDoc.getDoc().copyDocument(Constants.ASSET_TEMPORARY_SPACE + "." + pageName, context); Asset assetDoc = new Asset(newDoc, context); //assetDoc.init(copyOf, publishSpace); // FIXME: Trick this asset doc to believe that the doc we just set is already a clone, so that it returns it // uncloned on getDoc() to work around this bug: http://jira.xwiki.org/jira/browse/XWIKI-6885 assetDoc.cloned = true; assetDoc.getDoc().setCreator(context.getUser()); assetDoc.getDoc().setCustomClass(assetDoc.getClass().getName()); // Remove comments from copied asset assetDoc.removeObjects("XWiki.XWikiComments"); // Remove inherited CRS-reviews from copied asset assetDoc.removeObjects(Constants.ASSET_CURRIKI_REVIEW_CLASS); assetDoc.removeObjects(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS); BaseObject newLicenceObj = assetDoc.getDoc().getObject(Constants.ASSET_LICENCE_CLASS); if (newLicenceObj == null) { newLicenceObj = assetDoc.getDoc().newObject(Constants.ASSET_LICENCE_CLASS, context); } // Rights Holder should be by default the pretty name of the user added with the current rights holder (only if not already in the list) String newRightsHolder = context.getWiki().getLocalUserName(context.getUser(), null, false, context); String origRightsHolder = copyDoc.getDoc().getStringValue(Constants.ASSET_LICENCE_CLASS, Constants.ASSET_LICENCE_ITEM_RIGHTS_HOLDER); if (!origRightsHolder.matches("\\b" + newRightsHolder + "\\b")) { newRightsHolder += ", " + origRightsHolder; newLicenceObj.setStringValue(Constants.ASSET_LICENCE_ITEM_RIGHTS_HOLDER, newRightsHolder); } else { newLicenceObj.setStringValue(Constants.ASSET_LICENCE_ITEM_RIGHTS_HOLDER, origRightsHolder); } BaseObject newObjAsset = assetDoc.getDoc().getObject(Constants.ASSET_CLASS); if (newObjAsset == null) { newObjAsset = assetDoc.getDoc().newObject(Constants.ASSET_CLASS, context); } // Keep the information allowing to track where that asset came from newObjAsset.setStringValue(Constants.ASSET_CLASS_TRACKING, copyOf); // Clear rights objects otherwise this will trigger a remove object although these have never been saved assetDoc.getDoc().setObjects("XWiki.XWikiRights", new Vector<BaseObject>()); assetDoc.applyRightsPolicy(); //assetDoc.saveDocument(context.getMessageTool().get("curriki.comment.copiedsourceasset"), true); context.getWiki().saveDocument(assetDoc.getDoc(), context.getMessageTool().get("curriki.comment.copiedsourceasset"), true, context); return assetDoc; } protected void init(String parentAsset) throws XWikiException { init(parentAsset, null); } protected void init(String parentAsset, String publishSpace) throws XWikiException { assertCanEdit(); getDoc().setCreator(context.getUser()); inheritMetadata(parentAsset, publishSpace); getDoc().setCustomClass(getClass().getName()); setDefaultContent(); applyRightsPolicy(); } protected void setDefaultContent() throws XWikiException { assertCanEdit(); getDoc().setContent(""); } protected void initSubType() throws XWikiException { assertCanEdit(); // Empty for Superclass } public void addAttachment(InputStream iStream, String name) throws XWikiException, IOException { assertCanEdit(); addAttachment(name, iStream); } public String getDisplayTitle() { String title = getTitle(); return (title == null || title.length() == 0) ? Constants.ASSET_DISPLAYTITLE_UNTITLED : title; } public String getDescription() { String className = getActiveClass(); use(Constants.ASSET_CLASS); String description = (String) getValue(Constants.ASSET_CLASS_DESCRIPTION); if (className != null) { use(className); } return (description == null || description.length() == 0) ? "" : description; } public void changeOwnership(String newUser) { if (hasProgrammingRights()) { XWikiDocument assetDoc = getDoc(); assetDoc.setCreator(newUser); } } /** * Set the rights objects based on the current right setting * * @throws XWikiException */ public void applyRightsPolicy() throws XWikiException { applyRightsPolicy(null); } /** * Set the rights object based on the right in param or the current right setting if null * * @param right * @throws XWikiException */ public void applyRightsPolicy(String right) throws XWikiException { XWikiDocument assetDoc = getDoc(); assetDoc.removeObjects("XWiki.XWikiRights"); BaseObject assetObj = assetDoc.getObject(Constants.ASSET_CLASS); String rights; if (right == null) { // Use existing rights value rights = assetObj.getStringValue(Constants.ASSET_CLASS_RIGHT); LOG.debug("applyRightsPolicy: retrieved: '" + right + "'"); } else { LOG.debug("applyRightsPolicy: passed: '" + right + "'"); rights = right; assetObj.setStringValue(Constants.ASSET_CLASS_RIGHT, right); } // Make sure rights value is valid, default to PUBLIC if not if (rights == null || !(rights.equals(Constants.ASSET_CLASS_RIGHT_PUBLIC) || rights.equals(Constants.ASSET_CLASS_RIGHT_MEMBERS) || rights.equals(Constants.ASSET_CLASS_RIGHT_PRIVATE))) { LOG.warn("Rights is being defaulted. Got: '" + rights + "'"); rights = Constants.ASSET_CLASS_RIGHT_PUBLIC; assetObj.setStringValue(Constants.ASSET_CLASS_RIGHT, rights); } // Rights BaseObject rightObj; // If collection is user String usergroupfield = Constants.RIGHTS_CLASS_USER; String uservalue = ("".equals(assetDoc.getCreator())) ? context.getUser() : assetDoc.getCreator(); String usergroupvalue = uservalue; // If collection is group if (assetDoc.getSpace().startsWith(Constants.GROUP_COLLECTION_SPACE_PREFIX)) { usergroupfield = Constants.RIGHTS_CLASS_GROUP; usergroupvalue = assetDoc.getSpace().substring(5) + ".MemberGroup"; } // Always let the admin group have edit access rightObj = assetDoc.newObject(Constants.RIGHTS_CLASS, context); rightObj.setLargeStringValue(Constants.RIGHTS_CLASS_GROUP, Constants.RIGHTS_ADMIN_GROUP); rightObj.setStringValue("levels", "edit"); rightObj.setIntValue("allow", 1); // Always let the creator/group edit rightObj = assetDoc.newObject(Constants.RIGHTS_CLASS, context); // CURRIKI-2468 - allow creator to always edit/view their creations if (usergroupfield.equals(Constants.RIGHTS_CLASS_GROUP)) { rightObj.setLargeStringValue(Constants.RIGHTS_CLASS_USER, uservalue); } rightObj.setLargeStringValue(usergroupfield, usergroupvalue); rightObj.setStringValue("levels", "edit"); rightObj.setIntValue("allow", 1); if (rights.equals(Constants.ASSET_CLASS_RIGHT_PUBLIC)) { // Viewable by all and any member can edit rightObj = assetDoc.newObject(Constants.RIGHTS_CLASS, context); rightObj.setLargeStringValue(Constants.RIGHTS_CLASS_GROUP, Constants.RIGHTS_ALL_GROUP); rightObj.setStringValue("levels", "edit"); rightObj.setIntValue("allow", 1); } else if (rights.equals(Constants.ASSET_CLASS_RIGHT_MEMBERS)) { // Viewable by all, only user/group can edit } else { // rights == private, so only allow creator/group to view and edit rightObj = assetDoc.newObject(Constants.RIGHTS_CLASS, context); // CURRIKI-2468 - allow creator to always edit/view their creations if (usergroupfield.equals(Constants.RIGHTS_CLASS_GROUP)) { rightObj.setLargeStringValue(Constants.RIGHTS_CLASS_USER, uservalue); } rightObj.setLargeStringValue(usergroupfield, usergroupvalue); rightObj.setStringValue("levels", "view"); rightObj.setIntValue("allow", 1); // TODO: something here caused CURRIKI-3100 so the rest of the metadata could not get saved //private assets must be removed from review queue removeFromReviewQueue(); } } public Class<? extends Asset> getAssetClass() { if (hasAccessLevel("view")) { if (isFolder()) { if (isRootCollection()) { // Note that the Root Collection doesn't have an Asset Class // so it has to come before checking if no ASSET_CLASS return RootCollectionCompositeAsset.class; } else if (isCollection()) { return CollectionCompositeAsset.class; } else { return FolderCompositeAsset.class; } } String category = getCategory(); if (category == null) { return InvalidAsset.class; } // call the sub type asset manager to get more details AssetManager assetManager = (AssetManager) DefaultAssetManager.getAssetSubTypeManager(category); if (assetManager != null) { return assetManager.getAssetClass(); } else { // Last is just an attachment item if (doc.getAttachmentList().size() > 0) { return AttachmentAsset.class; } return Asset.class; } } else { return ProtectedAsset.class; } } public String getAssetType() { return getAssetClass().getSimpleName().replaceAll("Asset$", ""); } public boolean isFolder() { if (doc.getObjectNumbers(Constants.COMPOSITE_ASSET_CLASS) == 0) { return false; } else { // Work around a bug XWIKI-1624 // TODO: Remove the work-around once XWIKI-1624 is fixed List<BaseObject> compObjs = doc.getObjects(Constants.COMPOSITE_ASSET_CLASS); for (BaseObject compObj : compObjs) { if (compObj != null) { return true; } } } return false; } public boolean isCollection() { if (isFolder()) { com.xpn.xwiki.api.Object obj = getObject(Constants.COMPOSITE_ASSET_CLASS); if (getValue(Constants.COMPOSITE_ASSET_CLASS_TYPE, obj) .equals(Constants.COMPOSITE_ASSET_CLASS_TYPE_COLLECTION)) { return true; } } return false; } public boolean isRootCollection() { if (isFolder()) { com.xpn.xwiki.api.Object obj = getObject(Constants.COMPOSITE_ASSET_CLASS); if (getValue(Constants.COMPOSITE_ASSET_CLASS_TYPE, obj) .equals(Constants.COMPOSITE_ASSET_CLASS_TYPE_ROOT_COLLECTION)) { return true; } } return false; } static public Asset fetchAsset(String assetName, XWikiContext context) throws XWikiException { com.xpn.xwiki.api.XWiki xwikiApi = new com.xpn.xwiki.api.XWiki(context.getWiki(), context); Document doc = xwikiApi.getDocument(assetName); if (doc instanceof Asset) { return ((Asset) doc).as(null); } else { // Check to be sure the space exists (for add to Favorites) String space = assetName.replaceAll("\\..*$", ""); CollectionSpace.ensureExists(space, context); doc = xwikiApi.getDocument(assetName); if (doc instanceof Asset) { return ((Asset) doc).as(null); } else { throw new AssetException(AssetException.ERROR_ASSET_NOT_FOUND, "Asset " + assetName + " could not be found"); } } } static public Asset fetchAsset(String web, String page, XWikiContext context) throws XWikiException { return Asset.fetchAsset(web + "." + page, context); } public List<Property> getMetadata() { List<Property> md = new ArrayList<Property>(); com.xpn.xwiki.api.Object assetObj = getObject(Constants.ASSET_CLASS); for (java.lang.Object prop : assetObj.getPropertyNames()) { LOG.debug("Adding " + prop + " to metadata list"); md.add(assetObj.getProperty((String) prop)); } com.xpn.xwiki.api.Object licenseObj = getObject(Constants.ASSET_LICENCE_CLASS); for (java.lang.Object prop : licenseObj.getPropertyNames()) { LOG.debug("Adding " + prop + " to metadata list"); md.add(licenseObj.getProperty((String) prop)); } // Add title now that it isn't in the asset object BaseStringProperty baseProp = new BaseStringProperty(); baseProp.setName("title"); baseProp.setValue(getTitle()); Property prop = new Property(baseProp, context); md.add(prop); // Add creator baseProp = new BaseStringProperty(); baseProp.setName("creator"); baseProp.setValue(getCreator()); prop = new Property(baseProp, context); md.add(prop); // Add creator's name String creatorName = context.getWiki().getLocalUserName(context.getUser(), null, false, context); baseProp = new BaseStringProperty(); baseProp.setName("creatorName"); baseProp.setValue(creatorName); prop = new Property(baseProp, context); md.add(prop); // Add page's name baseProp = new BaseStringProperty(); baseProp.setName("assetpage"); baseProp.setValue(getFullName()); prop = new Property(baseProp, context); md.add(prop); // Add rights List Map<String, Boolean> rightsList = getRightsList(); String rightsListString = "{"; for (String right : rightsList.keySet()) { if (rightsListString.length() > 1) { rightsListString += ","; } rightsListString += "\"" + right + "\":" + (rightsList.get(right) ? "true" : "false"); } rightsListString += "}"; baseProp = new BaseStringProperty(); baseProp.setName("rightsList"); baseProp.setValue(rightsListString); prop = new Property(baseProp, context); md.add(prop); // Add subcategory baseProp = new BaseStringProperty(); baseProp.setName("subcategory"); baseProp.setValue(getCategorySubtype()); prop = new Property(baseProp, context); md.add(prop); // Add assetType to metadata Class assetType = getAssetClass(); baseProp = new BaseStringProperty(); baseProp.setName("fullAssetType"); baseProp.setValue(assetType.getCanonicalName()); prop = new Property(baseProp, context); md.add(prop); // And add shortAssetType to metadata String shortAssetType = assetType.getSimpleName(); if (!shortAssetType.equals("Asset")) { shortAssetType = getAssetType(); } baseProp = new BaseStringProperty(); baseProp.setName("assetType"); baseProp.setValue(shortAssetType); prop = new Property(baseProp, context); md.add(prop); baseProp = new BaseStringProperty(); baseProp.setName("revision"); baseProp.setValue(getVersion()); prop = new Property(baseProp, context); md.add(prop); return md; } public <A extends Asset> A as(Class<? extends Asset> wantedClass) throws XWikiException { return (A) as(wantedClass, false); } public <A extends Asset> A subclassAs(Class<? extends Asset> wantedClass) throws XWikiException { return (A) this.as(wantedClass, true); } public <A extends Asset> A as(Class<? extends Asset> wantedClass, boolean moreSpecific) throws XWikiException { // If this is already a subtype of the wanted class then just return this if (wantedClass != null && wantedClass.isAssignableFrom(this.getClass())) { return (A) this; } Class returnClass; // Make sure the determined type is a subclass of the wanted type Class<? extends Asset> assetType = getAssetClass(); if (!moreSpecific) { if (wantedClass == null) { // Return as whatever subtype it is returnClass = assetType; } else if (wantedClass.isAssignableFrom(assetType)) { // Return as the desired type returnClass = wantedClass; } else { throw new AssetException(AssetException.ERROR_ASSET_INCOMPATIBLE, "Document of type " + assetType + " cannot become " + wantedClass); } } else { // Subclass an Asset if (assetType.equals(wantedClass)) { // Already is that type, so don't initialize moreSpecific = false; returnClass = wantedClass; } else if (assetType.equals(Asset.class) && assetType.isAssignableFrom(wantedClass)) { // Turn into a specified subtype of Asset (initializing the subtype) returnClass = wantedClass; } else { throw new AssetException(AssetException.ERROR_ASSET_INCOMPATIBLE, "Document of type " + assetType + " cannot become " + wantedClass); } } Class[] parameterTypes = new Class[2]; parameterTypes[0] = XWikiDocument.class; parameterTypes[1] = XWikiContext.class; java.lang.Object[] initargs = new java.lang.Object[2]; initargs[0] = doc; initargs[1] = context; A subtyped; try { Constructor<A> constructor = returnClass.getConstructor(parameterTypes); subtyped = constructor.newInstance(initargs); if (moreSpecific) { subtyped.initSubType(); } } catch (Exception e) { throw new AssetException(AssetException.ERROR_ASSET_INCOMPATIBLE, "Document of type " + assetType + " cannot become " + wantedClass, e); } return subtyped; } public void inheritMetadata() throws XWikiException { inheritMetadata(null, null); } public void inheritMetadata(String parentAsset) throws XWikiException { inheritMetadata(parentAsset, null); } public void inheritMetadata(String parentAsset, String publishSpace) throws XWikiException { assertCanEdit(); XWikiDocument assetDoc = getDoc(); BaseObject assetObj = assetDoc.getObject(Constants.ASSET_CLASS); if (assetObj == null) { assetObj = assetDoc.getObject(Constants.ASSET_CLASS, true, context); } boolean licenceSet = false; BaseObject newLicenceObj = doc.getObject(Constants.ASSET_LICENCE_CLASS); if (newLicenceObj == null) { newLicenceObj = doc.newObject(Constants.ASSET_LICENCE_CLASS, context); } // CURRIKI-2451 - Make sure group rights are used by default if (publishSpace != null && publishSpace.startsWith(Constants.GROUP_COLLECTION_SPACE_PREFIX)) { String groupSpace = publishSpace.replaceFirst("^" + Constants.GROUP_COLLECTION_SPACE_PREFIX, Constants.GROUP_SPACE_PREFIX); String rights = Constants.ASSET_CLASS_RIGHT_PUBLIC; // TODO: This should probably be using the SpaceManager extension XWikiDocument groupSpaceDoc = context.getWiki() .getDocument(groupSpace + "." + Constants.GROUP_RIGHTS_PAGE, context); if (groupSpaceDoc != null) { // Note that the values for the group access defaults // DO NOT MATCH the values that need to be applied to the collection BaseObject rObj = groupSpaceDoc.getObject(Constants.GROUP_RIGHTS_CLASS); if (rObj != null) { String groupDefaultPrivs = rObj.getStringValue(Constants.GROUP_RIGHTS_PROPERTY); if (groupDefaultPrivs.equals(Constants.GROUP_RIGHT_PRIVATE)) { rights = Constants.ASSET_CLASS_RIGHT_PRIVATE; } else if (groupDefaultPrivs.equals(Constants.GROUP_RIGHT_PROTECTED)) { rights = Constants.ASSET_CLASS_RIGHT_MEMBERS; } else if (groupDefaultPrivs.equals(Constants.GROUP_RIGHT_PUBLIC)) { rights = Constants.ASSET_CLASS_RIGHT_PUBLIC; } // CURRIKI-4377 - inherit grade level, topic, language, and licence from group if (parentAsset == null || parentAsset.length() == 0) { // educationLevel List edLevels = rObj.getListValue(Constants.GROUP_DEFAULT_GRADE_PROPERTY); assetObj.setDBStringListValue(Constants.ASSET_CLASS_EDUCATIONAL_LEVEL, edLevels); // topic List topics = rObj.getListValue(Constants.GROUP_DEFAULT_TOPIC_PROPERTY); assetObj.setDBStringListValue(Constants.ASSET_CLASS_FRAMEWORK_ITEMS, topics); // language String lang = rObj.getStringValue(Constants.GROUP_DEFAULT_LANGUAGE_PROPERTY); assetObj.setStringValue(Constants.ASSET_CLASS_LANGUAGE, lang); // licence String licence = rObj.getStringValue(Constants.GROUP_DEFAULT_LICENCE_PROPERTY); newLicenceObj.setStringValue(Constants.ASSET_LICENCE_ITEM_LICENCE_TYPE, licence); licenceSet = true; } } } assetObj.setStringValue(Constants.ASSET_CLASS_RIGHT, rights); } XWikiDocument parentDoc = null; if (parentAsset != null && parentAsset.length() > 0) { parentDoc = context.getWiki().getDocument(parentAsset, context); if (parentDoc.isNew()) { throw new AssetException(AssetException.MODULE_PLUGIN_ASSET, AssetException.ERROR_ASSET_NOT_FOUND, "Parent asset not found"); } } // the Root collection does not have a proper asset class so we can't inherit from it if (parentDoc != null && parentDoc.getObject(Constants.ASSET_CLASS) != null && !parentDoc.getName().equals(Constants.ROOT_COLLECTION_PAGE)) { BaseObject parentAssetObj = (BaseObject) parentDoc.getObject(Constants.ASSET_CLASS).clone(); copyProperty(parentAssetObj, assetObj, Constants.ASSET_CLASS_EDUCATIONAL_LEVEL); copyProperty(parentAssetObj, assetObj, Constants.ASSET_CLASS_FRAMEWORK_ITEMS); copyProperty(parentAssetObj, assetObj, Constants.ASSET_CLASS_RIGHT); copyProperty(parentAssetObj, assetObj, Constants.ASSET_CLASS_KEYWORDS); copyProperty(parentAssetObj, assetObj, Constants.ASSET_CLASS_LANGUAGE); } // make sure default rights value is not empty (default to public) String rights = assetObj.getStringValue(Constants.ASSET_CLASS_RIGHT); if ((rights == null) || (rights.equals(""))) { assetObj.setStringValue(Constants.ASSET_CLASS_RIGHT, Constants.ASSET_CLASS_RIGHT_PUBLIC); } // the Root collection does not have an asset Licence class if (parentDoc != null && parentDoc.getObject(Constants.ASSET_LICENCE_CLASS) != null) { BaseObject parentLicenceObjAsset = parentDoc.getObject(Constants.ASSET_LICENCE_CLASS); copyProperty(parentLicenceObjAsset, newLicenceObj, Constants.ASSET_LICENCE_ITEM_LICENCE_TYPE); } else if (!licenceSet) { newLicenceObj.setStringValue(Constants.ASSET_LICENCE_ITEM_LICENCE_TYPE, Constants.ASSET_LICENCE_ITEM_LICENCE_TYPE_DEFAULT); } // Rights holder should be by default the pretty name of the user newLicenceObj.setStringValue(Constants.ASSET_LICENCE_ITEM_RIGHTS_HOLDER, context.getWiki().getLocalUserName(context.getUser(), null, false, context)); } /** * Make a folder in the current asset * @return * @throws XWikiException */ public FolderCompositeAsset makeFolder() throws XWikiException { return makeFolder(null); } /** * Make a folder in the current asset * @param page * @return * @throws XWikiException */ public FolderCompositeAsset makeFolder(String page) throws XWikiException { assertCanEdit(); FolderCompositeAsset asset = subclassAs(FolderCompositeAsset.class); if (page != null) { asset.addSubasset(page); } saveDocument(context.getMessageTool().get("curriki.comment.createfoldersourceasset"), true); return asset; } /** * Make a collection in the current asset * @return * @throws XWikiException */ public CollectionCompositeAsset makeCollection() throws XWikiException { return makeCollection(null); } /** * Make a composite collection in the current asset * @param page * @return * @throws XWikiException */ public CollectionCompositeAsset makeCollection(String page) throws XWikiException { assertCanEdit(); CollectionCompositeAsset asset = subclassAs(CollectionCompositeAsset.class); if (page != null) { asset.addSubasset(page); } saveDocument(context.getMessageTool().get("curriki.comment.createfoldersourceasset"), true); return asset; } /** * Make an attachment asset based on the type of attachment * This will call the appropriate subtype asset manager for further processing * @return an AttachmentAsset object * @throws XWikiException */ public AttachmentAsset processAttachment() throws XWikiException { assertCanEdit(); AttachmentAsset asset = subclassAs(AttachmentAsset.class); if (doc.getAttachmentList().size() > 0) { XWikiAttachment attach = doc.getAttachmentList().get(0); determineFileTypeAndCategory(attach); saveDocument(context.getMessageTool().get("curriki.comment.createtextsourceasset"), true); } return asset; } /** * This functions determines the file type and the category based on an attachment * @param attachment * @throws XWikiException */ protected void determineFileTypeAndCategory(XWikiAttachment attachment) throws XWikiException { String filename = attachment.getFilename(); String extension = (filename.lastIndexOf(".") != -1 ? filename.substring(filename.lastIndexOf(".") + 1).toLowerCase() : null); MimeTypePlugin mimePlugin = getMimeTypePlugin(); String filetype = mimePlugin.getFileType(extension, context); String category = mimePlugin.getCategory(filetype, context); XWikiDocument assetDoc = getDoc(); // set the attachment information BaseObject documentObject = assetDoc.getObject(Constants.ATTACHMENT_ASSET_CLASS, true, context); documentObject.setStringValue(Constants.ATTACHMENT_ASSET_FILE_TYPE, filetype); documentObject.setLongValue(Constants.ATTACHMENT_ASSET_FILE_SIZE, attachment.getFilesize()); // set the category BaseObject assetObj = assetDoc.getObject(Constants.ASSET_CLASS, true, context); assetObj.setStringValue(Constants.ASSET_CLASS_CATEGORY, category); // call the sub type asset manager to get more details AssetManager assetManager = DefaultAssetManager.getAssetSubTypeManager(category); if (assetManager != null) { assetManager.updateSubAssetClass(assetDoc, filetype, category, attachment, context); } } protected MimeTypePlugin getMimeTypePlugin() { MimeTypePlugin mimePlugin = (MimeTypePlugin) context.getWiki().getPlugin(MimeTypePlugin.PLUGIN_NAME, context); return mimePlugin; } public Boolean isPublished() { return !getSpace().equals("AssetTemp"); } public Asset publish(String space) throws XWikiException { return publish(space, true); } public Asset publish(String space, String name) throws XWikiException { return publish(space, name, true); } public Asset publish(String space, boolean checkSpace) throws XWikiException { String prettyName = context.getWiki().clearName(getTitle(), true, true, context); return publish(space, prettyName, checkSpace); } public Asset publish(String space, String name, boolean checkSpace) throws XWikiException { if (isPublished()) { throw new AssetException("This resource is already published"); } assertCanEdit(); XWikiDocument assetDoc = getDoc(); // Be sure we have a category first -- attachments can't get them automatically yet BaseObject obj = assetDoc.getObject(Constants.ASSET_CLASS); if (obj != null) { String category = obj.getStringValue(Constants.ASSET_CLASS_CATEGORY); if (category == null || category.length() == 0 || category.equals(Constants.ASSET_CATEGORY_UNKNOWN)) { processAttachment(); } } if (!space.startsWith(Constants.COLLECTION_PREFIX)) { throw new AssetException("You cannot publish to the space " + space); } if (!validate()) { throw new AssetException("Validation failed."); } if (checkSpace) { CollectionSpace.ensureExists(space, context); } // Let's choose a nice name for the page String prettyName = context.getWiki().clearName(name, true, true, context); //rename(space + "." + context.getWiki().getUniquePageName(space, prettyName.trim(), context), new ArrayList<String>()); // FIXME: this works totally by mistake, there is bug http://jira.xwiki.org/jira/browse/XWIKI-6885 which // normally breaks the doc returned by Document.getDoc(). But, lucky lucky, since this.clone = true from the // previous getDoc()s that were called, getDoc() will not re-clone again what we set in the following // line in this.doc but return it as is, which makes so that this.save() saves properly the copied attachments. // See the FIXME in copyTempAsset for a case when it doesn't work properly, a newly created Asset from a copied // XWikiDocument. this.doc = assetDoc.copyDocument( space + "." + context.getWiki().getUniquePageName(space, prettyName.trim(), context), context); applyRightsPolicy(); List<String> params = new ArrayList<String>(); params.add(assetDoc.getStringValue(Constants.ASSET_CLASS_CATEGORY)); save(context.getMessageTool().get("curriki.comment.finishcreatingsubasset", params)); if (isCollection()) { RootCollectionCompositeAsset root = CollectionSpace.getRootCollection(space, context); root.addSubasset(this.getFullName()); root.saveDocument(context.getMessageTool().get("curriki.comment.addtocollectionasset")); } // delete asset doc _at the end_, to be sure that all copying of the real document happens properly. // E.g. filesystem attachments fail to be copied properly if the original documnent is being deleted before // the copied document is saved context.getWiki().deleteDocument(assetDoc, context); return this; } public boolean validate() throws XWikiException { // Has the asset been subtyped ? if (getAssetClass().equals(Asset.class)) { throw new AssetException("This asset is not complete."); } // Other validation // Super's validate return super.validate(); } /** * * @param appropriatePedagogy * @param contentAccuracy * @param technicalCompletness * @return the calculated rating * @throws XWikiException */ public String calculateRating(String appropriatePedagogy, String contentAccuracy, String technicalCompletness) throws XWikiException { //validate that a value was selected in all categories if (appropriatePedagogy == null || "".equals(appropriatePedagogy) || contentAccuracy == null || "".equals(contentAccuracy) || technicalCompletness == null || "".equals(technicalCompletness)) { throw new AssetException( context.getMessageTool().get("curriki.crs.review.mustSelectAValueInAllCategories")); } //validate that only appropriatePedagogy is not rated //or all criteria are not rated if (!appropriatePedagogy.equals("0") && (contentAccuracy.equals("0") || technicalCompletness.equals("0"))) { throw new AssetException( context.getMessageTool().get("curriki.crs.review.notValidNotRatedCategorySelection")); } if (appropriatePedagogy.equals("0") && ((contentAccuracy.equals("0") && !technicalCompletness.equals("0")) || (!contentAccuracy.equals("0") && technicalCompletness.equals("0")))) { throw new AssetException( context.getMessageTool().get("curriki.crs.review.notValidNotRatedCategorySelection")); } int valueAppropriatePedagogy = Integer.parseInt(appropriatePedagogy); int valueContentAccuracy = Integer.parseInt(contentAccuracy); int valueTechnicalCompletness = Integer.parseInt(technicalCompletness); float numerator = valueAppropriatePedagogy + valueContentAccuracy + valueTechnicalCompletness; int rating; if (valueAppropriatePedagogy != 0) { rating = Math.round(numerator / 3); } else { rating = Math.round(numerator / 2); } return String.valueOf(rating); } /** * get the numbers of comments, the reviews are counted as a comment * @return comment numbers */ public Integer getCommentNumbers() { return getComments().size() + getObjectNumbers(Constants.ASSET_CURRIKI_REVIEW_CLASS); } /** * get a sorted by date list of comments * @return comments sorted by date */ public List getCommentsByDate() { List comments = getComments(); Collections.sort(comments, new CommentsSorter()); return comments; } /** * get a sorted by date list of comments * @return comments sorted by date */ public Long getRating() { use(Constants.ASSET_CLASS); return (Long) getValue(Constants.ASSET_CLASS_RATING); } /** * get a sorted by date list of comments * @param comment * @param rating * @return comments sorted by date */ public Integer addComment(String comment, long rating) throws XWikiException { int nb = newComment(comment, rating); saveWithProgrammingRights("added a new comment"); return nb; } /** * get a sorted by date list of comments * @param comment * @param rating * @return the new comment number */ public Integer newComment(String comment, long rating) throws XWikiException { BaseObject newCommentObj = getDoc().newObject(Constants.COMMENTS_CLASS, context); newCommentObj.setStringValue(Constants.COMMENTS_CLASS_AUTHOR, context.getUser()); newCommentObj.setLargeStringValue(Constants.COMMENTS_CLASS_COMMENT, comment); newCommentObj.setDateValue(Constants.COMMENTS_CLASS_DATE, new Date()); newCommentObj.setLongValue(Constants.COMMENTS_CLASS_RATING, rating); if (rating > 0) { // update asset rating BaseObject assetObj = getDoc().getObject(Constants.ASSET_CLASS); long rating_sum = assetObj.getLongValue(Constants.ASSET_CLASS_RATING_SUM); long rating_count = assetObj.getLongValue(Constants.ASSET_CLASS_RATING_COUNT); rating_sum += rating; rating_count++; long asset_rating = (rating_sum / rating_count) + (rating_sum % rating_count) * 2 / rating_count; assetObj.setLongValue(Constants.ASSET_CLASS_RATING_SUM, rating_sum); assetObj.setLongValue(Constants.ASSET_CLASS_RATING_COUNT, rating_count); assetObj.setLongValue(Constants.ASSET_CLASS_RATING, asset_rating); } return newCommentObj.getNumber(); } /** * get a sorted by date list of comments * @param nb * @return comment object by number */ public Object getComment(int nb) { return getObject(Constants.COMMENTS_CLASS, nb); } /** * get a sorted by date list of comments * @param nb * @return review object by number */ public Object getReview(int nb) { return getObject(Constants.ASSET_CURRIKI_REVIEW_CLASS, nb); } /** * get a sorted by date list of comments * @return array of review objects */ public Vector<Object> getReviews() { return getObjects(Constants.ASSET_CURRIKI_REVIEW_CLASS); } /** * get a sorted by date list of comments * @return a list of reviews sorted by date, descendent */ public List getReviewsByDate() { List reviews = getReviews(); Collections.sort(reviews, new CommentsSorter()); return reviews; } /** * get a sorted by date list of comments * @return the latest review object */ public Object getLastReview() { List reviews = getReviews(); if (reviews.isEmpty()) { return null; } return (com.xpn.xwiki.api.Object) Collections.max(reviews, new CommentsSorter()); } public boolean canBeNominatedOrReviewed() { use(Constants.ASSET_CLASS); String rights = (String) getValue(Constants.ASSET_CLASS_RIGHT); //false if access privilege is PRIVATE if (rights != null && rights.equals(Constants.ASSET_CLASS_RIGHT_PRIVATE)) { return false; } //false if CRS.CurrikiReviewStatusClass.status == P (Partner) use(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS); String status = (String) getValue(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_STATUS); if (status != null && status.equals(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_STATUS_PARTNER)) { return false; } //false if BFCS is SPECIALCHECKREQUIRED,IMPROVEMENTREQUIRED or DELETEDFROMMEMBERACCESS use(Constants.ASSET_CLASS); String fcStatus = (String) getValue(Constants.ASSET_BFCS_STATUS); if (fcStatus != null && !fcStatus.equals("")) { if (fcStatus.equals(Constants.ASSET_BFCS_STATUS_SPECIALCHECKREQUIRED) || fcStatus.equals(Constants.ASSET_BFCS_STATUS_IMPROVEMENTREQUIRED) || fcStatus.equals(Constants.ASSET_BFCS_STATUS_DELETEDFROMMEMBERACCESS)) { return false; } } return true; } /** * If BFCS other than OK was applied or the resource is set to Private, * the resource should be removed from the Review Queue and no longer show Nominate or Review links. */ public void checkReviewQueue() { use(Constants.ASSET_CLASS); String fcStatus = (String) getValue(Constants.ASSET_BFCS_STATUS); if (fcStatus != null && !fcStatus.equals("")) { if (fcStatus.equals(Constants.ASSET_BFCS_STATUS_SPECIALCHECKREQUIRED) || fcStatus.equals(Constants.ASSET_BFCS_STATUS_IMPROVEMENTREQUIRED) || fcStatus.equals(Constants.ASSET_BFCS_STATUS_DELETEDFROMMEMBERACCESS)) { removeFromReviewQueue(); return; } } String rights = (String) getValue(Constants.ASSET_CLASS_RIGHT); //false if access privilege is PRIVATE if (rights != null && rights.equals(Constants.ASSET_CLASS_RIGHT_PRIVATE)) { removeFromReviewQueue(); } } /** * Remove the asset from the review queue */ public void removeFromReviewQueue() { // TODO: Something here caused CURRIKI-3100 making the rest of the metadata not get saved // Could be the use of "use" as that is not being used elsewhere here ? //use(Constants.ASSET_CURRIKI_REVIEW_CLASS); XWikiDocument assetDoc = getDoc(); BaseObject obj = assetDoc.getObject(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS); if (obj != null) { Integer reviewpending = obj.getIntValue("reviewpending"); if (reviewpending != null && reviewpending.equals(1)) { obj.setIntValue("reviewpending", 0); } } } /** * Nominate a resource for review * @param comment * @return * @throws XWikiException */ public Asset nominate(String comment) throws XWikiException { XWikiDocument assetDoc = getDoc(); BaseObject obj = assetDoc.getObject(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS); String user = context.getUser(); if (obj == null) { obj = assetDoc.newObject(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS, context); } obj.setStringValue(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_NOMINATION_USER, user); obj.setDateValue(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_NOMINATION_DATE, new Date()); obj.setLargeStringValue(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_NOMINATION_COMMENT, comment); obj.setIntValue(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_REVIEW_PENDING, 1); saveWithProgrammingRights("save CRS nomination"); return this; } /** * Auto nominate a resource for review when rating > 3 * @return * @throws XWikiException */ public Asset autoNominate() throws XWikiException { XWikiDocument assetDoc = getDoc(); BaseObject assetObj = assetDoc.getObject(Constants.ASSET_CLASS); long rating = assetObj.getLongValue(Constants.ASSET_CLASS_RATING); // compute autonominate if (rating > 3) { String rights = assetObj.getStringValue(Constants.ASSET_CLASS_RIGHT); BaseObject reviewObj = assetDoc.getObject(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS); String status = ""; int review_pending = 0; if (reviewObj != null) { status = reviewObj.getStringValue(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_STATUS); review_pending = reviewObj.getIntValue(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_REVIEW_PENDING, 0); } if (review_pending == 0 && !StringUtils.equals(rights, Constants.ASSET_CLASS_RIGHT_PRIVATE) && (StringUtils.equals(status, Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS_STATUS_NOT_RATED) || StringUtils.isEmpty(status))) { nominate(context.getMessageTool().get("curriki.crs.nominate.automatic_due_to_rating.note")); } } return this; } /** * Data Model migration code * Determines if an asset is in the latest format * @return boolean true if latest format */ public boolean isLatestVersion() throws XWikiException { if (getObject(Constants.OLD_ASSET_CLASS) != null) return false; if (getObject(Constants.OLD_EXTERNAL_ASSET_CLASS) != null) return false; return true; } /** * Data Model Migration Code * @param newAssetObject * @param oldAssetObject * @param propname */ private void updateObject(Object newAssetObject, Object oldAssetObject, String propname) { updateObject(newAssetObject, oldAssetObject, propname, propname); } /** * Data Model Migration Code * @param newAssetObject * @param oldAssetObject * @param newpropname * @param oldpropname */ private void updateObject(Object newAssetObject, Object oldAssetObject, String newpropname, String oldpropname) { if (LOG.isDebugEnabled()) LOG.debug("CURRIKI CONVERTER: updating property " + newpropname); use(oldAssetObject); java.lang.Object value = getValue(oldpropname); use(newAssetObject); set(newpropname, value); if (LOG.isDebugEnabled()) LOG.debug("CURRIKI CONVERTER: updated property value is " + newAssetObject.get(newpropname)); } /** * Conversion of the category and sub type classes * @throws XWikiException */ private void setNewCategoryAndClass() throws XWikiException { if (LOG.isDebugEnabled()) LOG.debug("CURRIKI CONVERTER: running setNewCategoryAndClass"); // Get Asset objects Object oldAssetObject = getObject(Constants.OLD_ASSET_CLASS); // get old category if (oldAssetObject != null) use(oldAssetObject); String oldCategory = (String) getValue(Constants.ASSET_CLASS_CATEGORY); String newCategory = ""; if (oldCategory == null) oldCategory = ""; // transforming category of type text if (oldCategory.equals(Constants.OLD_CATEGORY_COLLECTION) || (getObject(Constants.OLD_COMPOSITE_ASSET_CLASS) != null)) { newCategory = Constants.ASSET_CATEGORY_COLLECTION; // we don't need the composite asset object Object oldCompositeAssetObject = getObject(Constants.OLD_COMPOSITE_ASSET_CLASS); Object newCompositeAssetObject = getObject(Constants.COMPOSITE_ASSET_CLASS, true); if (oldCompositeAssetObject != null) { removeObject(oldCompositeAssetObject); updateObject(newCompositeAssetObject, oldCompositeAssetObject, Constants.COMPOSITE_ASSET_CLASS_TYPE); use(newCompositeAssetObject); if (Constants.OLD_COMPOSITE_ASSET_CLASS_TYPE_SUBFOLDER .equals(getValue(Constants.COMPOSITE_ASSET_CLASS_TYPE))) set(Constants.COMPOSITE_ASSET_CLASS_TYPE, Constants.COMPOSITE_ASSET_CLASS_TYPE_SUBFOLDER); } else { if (LOG.isErrorEnabled()) LOG.error( "CURRIKI ASSET CONVERTER ERROR: asset declared collection has no composite class for asset " + getFullName()); } // convert sub-assets List subassets = getObjects(Constants.OLD_SUBASSET_CLASS); if ((subassets != null) && (subassets.size() > 0)) { for (int i = 0; i < subassets.size(); i++) { Object oldSubasset = (Object) subassets.get(i); Object newSubasset = newObject(Constants.SUBASSET_CLASS); updateObject(newSubasset, oldSubasset, Constants.SUBASSET_CLASS_ORDER); updateObject(newSubasset, oldSubasset, Constants.SUBASSET_CLASS_PAGE); } removeObjects(Constants.OLD_SUBASSET_CLASS); } // convert reorder info List reorderedlist = getObjects(Constants.OLD_COLLECTION_REORDERED_CLASS); if ((reorderedlist != null) && (reorderedlist.size() > 0)) { for (int i = 0; i < reorderedlist.size(); i++) { Object oldReorderdObject = (Object) reorderedlist.get(i); Object newReorderdObject = newObject(Constants.COLLECTION_REORDERED_CLASS); updateObject(newReorderdObject, oldReorderdObject, Constants.COLLECTION_REORDERED_CLASS_REORDERD); } removeObjects(Constants.OLD_COLLECTION_REORDERED_CLASS); } } else if (oldCategory.equals(Constants.OLD_CATEGORY_LINK) || (getObject(Constants.OLD_EXTERNAL_ASSET_CLASS) != null)) { newCategory = Constants.ASSET_CATEGORY_EXTERNAL; Object newExternalAssetObject = getObject(Constants.EXTERNAL_ASSET_CLASS, true); Object oldExternalAssetObject = getObject(Constants.OLD_EXTERNAL_ASSET_CLASS); if (oldExternalAssetObject != null) { updateObject(newExternalAssetObject, oldExternalAssetObject, Constants.EXTERNAL_ASSET_LINK, Constants.OLD_EXTERNAL_ASSET_LINK); removeObject(oldExternalAssetObject); } // make sure we don't have this object anymore since a bad previous migration could have caused it Object attachmentAssetObject = getObject(Constants.ATTACHMENT_ASSET_CLASS); if (attachmentAssetObject != null) removeObject(attachmentAssetObject); // make sure we don't have this object anymore since a bad previous migration could have caused it Object imageAssetObject = getObject(Constants.IMAGE_ASSET_CLASS); if (imageAssetObject != null) removeObject(imageAssetObject); // make sure we don't have this object anymore since it could exist previously for external assets Object oldImageAssetObject = getObject(Constants.OLD_MIMETYPE_PICTURE_CLASS); if (oldImageAssetObject != null) removeObject(oldImageAssetObject); } else if (oldCategory.equals(Constants.OLD_CATEGORY_TEXT) || (getObject(Constants.OLD_TEXT_ASSET_CLASS) != null)) { Object newTextAssetObject = getObject(Constants.TEXT_ASSET_CLASS, true); set(Constants.TEXT_ASSET_SYNTAX, Constants.TEXT_ASSET_SYNTAX_TEXT); Object oldTextAssetObject = getObject(Constants.OLD_TEXT_ASSET_CLASS); if (oldTextAssetObject != null) { newCategory = Constants.ASSET_CATEGORY_TEXT; use(oldTextAssetObject); Long type = (Long) getValue(Constants.OLD_TEXT_ASSET_CLASS_TYPE); use(oldTextAssetObject); String content = (String) getValue(Constants.OLD_TEXT_ASSET_CLASS_TEXT); if (content == null) setContent(""); else setContent(content); use(newTextAssetObject); if ((type == null) || type == 0) { set(Constants.TEXT_ASSET_SYNTAX, Constants.TEXT_ASSET_SYNTAX_XWIKI1); } else if (type == 1) { set(Constants.TEXT_ASSET_SYNTAX, Constants.TEXT_ASSET_SYNTAX_XHTML1); } else if (type == 2) { set(Constants.TEXT_ASSET_SYNTAX, Constants.TEXT_ASSET_SYNTAX_XWIKI1); } else { set(Constants.TEXT_ASSET_SYNTAX, Constants.TEXT_ASSET_SYNTAX_TEXT); } removeObject(oldTextAssetObject); } } else if (oldCategory.equals(Constants.OLD_CATEGORY_ARCHIVE) || (getObject(Constants.OLD_MIMETYPE_ARCHIVE_CLASS) != null)) { newCategory = Constants.ASSET_CATEGORY_ARCHIVE; getObject(Constants.ATTACHMENT_ASSET_CLASS, true); Object oldArchiveAssetObject = getObject(Constants.OLD_MIMETYPE_ARCHIVE_CLASS); Object newArchiveAssetObject = getObject(Constants.ARCHIVE_ASSET_CLASS, true); if (oldArchiveAssetObject != null) { updateObject(newArchiveAssetObject, oldArchiveAssetObject, Constants.ARCHIVE_ASSET_START_FILE, Constants.OLD_MIMETYPE_ARCHIVE_CLASS_DEFAULT_FILE); // we need to remove the old archive object removeObject(oldArchiveAssetObject); } } else if (oldCategory.equals(Constants.OLD_CATEGORY_ANIMATION)) { newCategory = Constants.ASSET_CATEGORY_INTERACTIVE; getObject(Constants.ATTACHMENT_ASSET_CLASS, true); } else if (oldCategory.equals(Constants.OLD_CATEGORY_IMAGE)) { newCategory = Constants.ASSET_CATEGORY_IMAGE; getObject(Constants.ATTACHMENT_ASSET_CLASS, true); Object oldImageAssetObject = getObject(Constants.OLD_MIMETYPE_PICTURE_CLASS); Object newImageAssetObject = getObject(Constants.IMAGE_ASSET_CLASS, true); if (oldImageAssetObject != null) { updateObject(newImageAssetObject, oldImageAssetObject, Constants.IMAGE_ASSET_WIDTH); updateObject(newImageAssetObject, oldImageAssetObject, Constants.IMAGE_ASSET_HEIGHT); } // we need to remove the old image object removeObject(oldImageAssetObject); } else if (getObject(Constants.OLD_VIDITALK_CLASS) != null) { newCategory = Constants.ASSET_CATEGORY_VIDEO; getObject(Constants.ATTACHMENT_ASSET_CLASS, true); // transfer video id Object newVideoAssetObject = getObject(Constants.VIDEO_ASSET_CLASS, true); Object oldVideoAssetObject = getObject(Constants.OLD_VIDITALK_CLASS); if (oldVideoAssetObject != null) { use(oldVideoAssetObject); String videoId = (String) getValue(Constants.OLD_VIDITALK_CLASS_VIDEO_ID); use(newVideoAssetObject); if (videoId != null) { set(Constants.VIDEO_ASSET_ID, videoId); set(Constants.VIDEO_ASSET_PARTNER, Constants.VIDEO_ASSET_PARTNER_VIDITALK); } removeObject(oldVideoAssetObject); } } else if (oldCategory.equals(Constants.OLD_CATEGORY_AUDIO)) { // we cannot determine type between audio and video newCategory = null; getObject(Constants.ATTACHMENT_ASSET_CLASS, true); } else if (oldCategory.equals(Constants.OLD_CATEGORY_DOCUMENT)) { newCategory = Constants.ASSET_CATEGORY_DOCUMENT; getObject(Constants.ATTACHMENT_ASSET_CLASS, true); } // update document (attachment) assets. // We need to do this either because the document use to be declared as a Document // We also need to do this if the document is not a text asset and there is an attachment Object newDocumentAssetObject = getObject(Constants.ATTACHMENT_ASSET_CLASS); if ((!Constants.ASSET_CATEGORY_TEXT.equals(newCategory) && (getAttachmentList().size() > 0)) || (newDocumentAssetObject != null)) { updateObject(newDocumentAssetObject, oldAssetObject, Constants.ATTACHMENT_ASSET_ALT_TEXT, Constants.OLD_ASSET_CLASS_ALT_TEXT); updateObject(newDocumentAssetObject, oldAssetObject, Constants.ATTACHMENT_ASSET_CAPTION_TEXT, Constants.OLD_ASSET_CLASS_CAPTION_TEXT); XWikiAttachment attachment = (doc.getAttachmentList().size() > 0) ? doc.getAttachmentList().get(0) : null; if (attachment != null) determineFileTypeAndCategory(attachment); else { if (LOG.isErrorEnabled()) LOG.error( "CURRIKI ASSET CONVERTER ERROR: attachment missing in document asset " + getFullName()); } // temporary log for debugging if (LOG.isInfoEnabled()) { LOG.info("CURRIKI ASSET CONVERTER: detected category is " + getCategory()); } // we should have found the same category through conversion if (newCategory != null) { if (!newCategory.equals(getCategory())) { if (LOG.isErrorEnabled()) LOG.error("CURRIKI ASSET CONVERTER ERROR: newCategory " + getCategory() + " different than converted category " + newCategory + " for asset " + getFullName()); } // let's fallback to the new Category if (getCategory() == Constants.ASSET_CATEGORY_UNKNOWN) setCategory(newCategory); } // make sure we don't have this object anymore Object oldArchiveAssetObject = getObject(Constants.OLD_MIMETYPE_ARCHIVE_CLASS); if (oldArchiveAssetObject != null) removeObject(oldArchiveAssetObject); } else { // for text, collection, external, collection assets setCategory(newCategory); } } /** * Hacked function needed by the Curriki Plugin * This is needed for the data model migration in case of a rollback item */ protected void setAlreadyCloned() { cloned = true; } /** * Data Model Migration Code * Convert an asset from the old format to the new format * */ public boolean convert() throws Exception { if (!hasProgrammingRights()) { java.lang.Object[] args = { getFullName() }; throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED, "Access denied with no programming rights document {0}", null, args); } if (convertWithoutSave()) { try { // Saving as a minor edit // We bypass the public api so that the author is not updated // We reset setContentDirty so that the content date does not change getDoc().setContentDirty(false); context.getWiki().saveDocument(getDoc(), context.getMessageTool().get("curriki.comment.datamodelmigration"), true, context); return true; } catch (Exception e) { if (LOG.isErrorEnabled()) LOG.error("CURRIKI ASSET CONVERTER ERROR: error saving converted asset " + getFullName(), e); throw e; } } return false; } /** * Data Model Migration Code * Convert an asset from the old format to the new format */ public boolean convertWithoutSave() throws Exception { if (isLatestVersion()) { if (LOG.isDebugEnabled()) LOG.debug("CURRIKI CONVERTER: asset already converted " + getFullName()); return false; } try { // Convert Asset Class // we need to make sure we are working on the modified object // because getObject(classname, boolean) does not do the job right getDoc(); Object oldAssetObject = getObject(Constants.OLD_ASSET_CLASS); Object newAssetObject = getObject(Constants.ASSET_CLASS, true); if (LOG.isDebugEnabled()) LOG.debug("CURRIKI CONVERTER: running convert on asset " + getFullName()); if (oldAssetObject != null) { // set title use(oldAssetObject); String title = (String) getValue(Constants.OLD_ASSET_CLASS_TITLE); if (title != null) setTitle(title); setContent(""); // transfer unchanged fields updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_DESCRIPTION); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_FRAMEWORK_ITEMS); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_HIDDEN_FROM_SEARCH); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_KEYWORDS); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_LANGUAGE); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_RIGHT); // transfer tracking field updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_TRACKING); // transfer file check fields updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_FCSTATUS); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_FCREVIEWER); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_FCDATE); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_FCNOTES); // transfer changed fields updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_INSTRUCTIONAL_COMPONENT, Constants.OLD_ASSET_CLASS_INSTRUCTIONAL_COMPONENT); updateObject(newAssetObject, oldAssetObject, Constants.ASSET_CLASS_EDUCATIONAL_LEVEL, Constants.OLD_ASSET_CLASS_EDUCATIONAL_LEVEL); } // create new category field setNewCategoryAndClass(); if (oldAssetObject != null) { // remove old asset object removeObject(oldAssetObject); } // Convert License Class Object oldLicenseObject = getObject(Constants.OLD_ASSET_LICENCE_CLASS); Object newLicenseObject = getObject(Constants.ASSET_LICENCE_CLASS, true); if (oldLicenseObject != null) { updateObject(newLicenseObject, oldLicenseObject, Constants.ASSET_LICENCE_ITEM_LICENCE_TYPE, Constants.OLD_ASSET_LICENCE_ITEM_LICENCE_TYPE); updateObject(newLicenseObject, oldLicenseObject, Constants.ASSET_LICENCE_ITEM_RIGHTS_HOLDER); updateObject(newLicenseObject, oldLicenseObject, Constants.ASSET_LICENCE_ITEM_EXTERNAL_RIGHTS_HOLDER); updateObject(newLicenseObject, oldLicenseObject, Constants.ASSET_LICENCE_ITEM_EXPIRY_DATE); // remove old asset license object removeObject(oldLicenseObject); } return true; } catch (Exception e) { if (LOG.isErrorEnabled()) LOG.error("CURRIKI ASSET CONVERTER ERROR: error converting asset " + getFullName(), e); throw new XWikiException(XWikiException.MODULE_XWIKI_PLUGINS, XWikiException.ERROR_XWIKI_UNKNOWN, "Curriki asset conversion exception", e); } } /** * Returns the first attachment in the list * @return */ protected Attachment getFirstAttachment() { List list = getAttachmentList(); if (list.size() == 0) return null; Attachment attach = (Attachment) list.get(0); return attach; } /** * This function allows to replace the attachment from a file upload * It also reruns the determine filetype and category function * @return */ public boolean replaceAttachment() { if (!hasProgrammingRights()) return false; if (LOG.isDebugEnabled()) LOG.debug("Getting first attachment"); Attachment attach = getFirstAttachment(); XWikiAttachment attachment = (attach == null) ? null : attach.getAttachment(); FileUploadPlugin fileupload = (FileUploadPlugin) context.getWiki().getPlugin("fileupload", context); String name = "file"; boolean newFileName = false; try { // getting the name for the newly uploaded file String fname = fileupload.getFileName(name, context); if (!StringUtils.isEmpty(fname) && fname.length() > 0) { int i = fname.lastIndexOf("\\"); if (i == -1) { i = fname.lastIndexOf("/"); } String filename = fname.substring(i + 1); filename = filename.replaceAll("\\+", " "); boolean replaceAttach = false; // if the attachment exists and it has a different name if (attachment != null && !filename.equals(attachment.getFilename())) { replaceAttach = true; // delete it if (LOG.isDebugEnabled()) { LOG.debug("Deleting previous attachment"); } doc.deleteAttachment(attachment, context); } if (attachment == null || replaceAttach) { if (LOG.isDebugEnabled() && attachment == null) { LOG.debug("Creating attachment from scratch attachment"); } // create a new attachment to add the new content in it newFileName = true; attachment = new XWikiAttachment(); doc.getAttachmentList().add(attachment); attachment.setDoc(doc); } // now save the attachment under the new name if (LOG.isDebugEnabled()) { LOG.debug("Saving attachment"); } attachment.setContent(fileupload.getFileItemInputStream(name, context)); attachment.setFilename(filename); attachment.setAuthor(context.getUser()); doc.setAuthor(context.getUser()); // if it's a new file name we should run the category updater if (newFileName) determineFileTypeAndCategory(attachment); return true; } else { // No files in fileupload plugin, no replacements return false; } } catch (XWikiException e) { e.printStackTrace(); context.put("exception", e); return false; } catch (IOException e) { // thrown when the fileupload fails to read the input stream to the content of the attachment properly e.printStackTrace(); context.put("exception", e); // it means that the uploaded file for the new attachment was not found or something went wrong reading it if (LOG.isDebugEnabled()) LOG.debug("No attachment found or exception while reading the input stream from the attachment."); return false; } } public boolean unLock() { try { XWikiLock lock = doc.getLock(context); if (lock != null) doc.removeLock(context); return true; } catch (XWikiException e) { return false; } } public class CommentsSorter implements Comparator { public int compare(java.lang.Object o1, java.lang.Object o2) { com.xpn.xwiki.api.Object o1a = (com.xpn.xwiki.api.Object) o1; com.xpn.xwiki.api.Object o2a = (com.xpn.xwiki.api.Object) o2; if (o1 == null) return 1; if (o2 == null) return -1; Date d1 = (Date) o1a.getProperty("date").getValue(); Date d2 = (Date) o2a.getProperty("date").getValue(); if (d1 == null) return 1; if (d2 == null) return -1; return d1.compareTo(d2); } } }