/* * 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.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); // 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.init(copyOf, publishSpace); 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(); XWikiAttachment att = addAttachment(name, iStream).getAttachment(); getDoc().saveAttachmentContent(att, context); } 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 { 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); if(edLevels!=null) edLevels = new ArrayList(edLevels); assetObj.setDBStringListValue(Constants.ASSET_CLASS_EDUCATIONAL_LEVEL, edLevels); // topic List topics = rObj.getListValue(Constants.GROUP_DEFAULT_TOPIC_PROPERTY); if(topics!=null) topics = new ArrayList(topics); 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); } public List getCommentsByDate() { List comments = getComments(); Collections.sort(comments, new CommentsSorter()); return comments; } 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 comments * @return * @throws XWikiException */ public Asset nominate(String comments) throws XWikiException { XWikiDocument assetDoc = getDoc(); BaseObject obj = assetDoc.getObject(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS); String suser = context.getUser(); if (obj==null) { obj = assetDoc.newObject(Constants.ASSET_CURRIKI_REVIEW_STATUS_CLASS, context); obj.setIntValue("number",0); } obj.setStringValue("nomination_user", suser); obj.setDateValue("nomination_date", new Date()); obj.setLargeStringValue("nomination_comment", comments); obj.setIntValue("reviewpending", 1); saveWithProgrammingRights("save CRS nomination"); 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; byte[] data = new byte[0]; try { data = fileupload.getFileItemData(name, context); if ((data!=null)&&(data.length>0)) { if (LOG.isDebugEnabled()) LOG.debug("Found data for attachment"); String fname = fileupload.getFileName(name, context); int i = fname.lastIndexOf("\\"); if (i==-1) i = fname.lastIndexOf("/"); String filename = fname.substring(i+1); filename = filename.replaceAll("\\+"," "); if (attachment==null) { if (LOG.isDebugEnabled()) LOG.debug("Creating attachment from scratch attachment"); newFileName = true; attachment = new XWikiAttachment(); doc.getAttachmentList().add(attachment); // Add the attachment to the document attachment.setDoc(doc); } else if (attachment.getFilename()!=filename) { // filename is different we need to delete the previous attachment if (LOG.isDebugEnabled()) LOG.debug("Deleting previous attachment"); newFileName = true; doc.deleteAttachment(attachment, context); attachment.setFilename(filename); doc.getAttachmentList().add(attachment); // Add the attachment to the document attachment.setDoc(doc); } // now save the attachment under the new name if (LOG.isDebugEnabled()) LOG.debug("Saving attachment"); attachment.setContent(data); attachment.setFilename(filename); attachment.setAuthor(context.getUser()); doc.setAuthor(context.getUser()); doc.saveAttachmentContent(attachment, context); // if it's a new file name we should run the category updater if (newFileName) determineFileTypeAndCategory(attachment); return true; } else { // no attachment if (LOG.isDebugEnabled()) LOG.debug("No attachment found"); return false; } } catch (XWikiException e) { e.printStackTrace(); context.put("exception", e); 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); } } }