package org.curriki.xwiki.plugin.asset.composite;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.web.XWikiMessageTool;
import com.xpn.xwiki.api.Document;
import com.xpn.xwiki.objects.BaseObject;
import org.curriki.xwiki.plugin.asset.Asset;
import org.curriki.xwiki.plugin.asset.Constants;
import org.curriki.xwiki.plugin.asset.AssetException;
import org.curriki.xwiki.plugin.asset.other.ProtectedAsset;
import org.curriki.xwiki.plugin.asset.other.InvalidAsset;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Collections;
/**
*/
public abstract class CompositeAsset extends Asset {
private static final Log LOG = LogFactory.getLog(RootCollectionCompositeAsset.class);
public final static String CATEGORY_NAME = Constants.ASSET_CATEGORY_COLLECTION;
public CompositeAsset(XWikiDocument doc, XWikiContext context) {
super(doc, context);
}
protected void initSubType() throws XWikiException {
super.initSubType();
BaseObject obj = doc.newObject(Constants.COMPOSITE_ASSET_CLASS, context);
obj.setStringValue(Constants.COMPOSITE_ASSET_CLASS_TYPE, compositeAssetType());
determineCategory();
setDefaultContent();
}
protected void setDefaultContent() throws XWikiException {
assertCanEdit();
doc.setContent("");
}
@Override
public String getCategorySubtype() {
return compositeAssetType();
}
abstract protected String compositeAssetType();
public Map<String,Object> getCompositeInfo() {
Map<String,Object> docInfo = new HashMap<String, Object>();
addSubinfo(docInfo, this);
// Children
List<Map<String,Object>> subList = getSubassetsInfo();
if (subList.size() > 0) {
docInfo.put("children", subList);
}
return docInfo;
}
protected Map<String,Object> addEmptySubinfo(Map<String,Object> subInfo, String assetType) {
subInfo.put("displayTitle", "");
subInfo.put("description", "");
subInfo.put("revision", "");
subInfo.put("fwItems", new String[]{});
subInfo.put("levels", new String[]{});
subInfo.put("category", "");
subInfo.put("subcategory", "");
subInfo.put("ict", "");
subInfo.put("assetType", assetType);
Map<String,Boolean> rightsInfo = new HashMap<String, Boolean>(3);
rightsInfo.put("view", false);
rightsInfo.put("edit", false);
rightsInfo.put("delete", false);
subInfo.put("rights", rightsInfo);
return subInfo;
}
protected Map<String,Object> addSubinfo(Map<String,Object> subInfo, Document doc) {
if (doc instanceof Asset) {
Asset aDoc = (Asset) doc;
subInfo.put("displayTitle", aDoc.getDisplayTitle());
subInfo.put("description", aDoc.getDescription());
subInfo.put("revision", aDoc.getVersion());
subInfo.put("fwItems", aDoc.getValue(Constants.ASSET_CLASS_FRAMEWORK_ITEMS));
subInfo.put("levels", aDoc.getValue(Constants.ASSET_CLASS_EDUCATIONAL_LEVEL));
subInfo.put("category", aDoc.getCategory());
subInfo.put("subcategory", aDoc.getCategorySubtype());
subInfo.put("ict", aDoc.getValue(Constants.ASSET_CLASS_INSTRUCTIONAL_COMPONENT));
if (aDoc instanceof CompositeAsset) {
subInfo.put("collectionType", ((CompositeAsset) aDoc).compositeAssetType());
}
subInfo.put("assetType", aDoc.getAssetType());
subInfo.put("rights", aDoc.getRightsList());
}
return subInfo;
}
public List<Map<String, Object>> getSubassetsInfo() {
List<BaseObject> objs = doc.getObjects(Constants.SUBASSET_CLASS);
if (objs != null ) {
List<Map<String,Object>> subList = new ArrayList<Map<String,Object>>(objs.size());
for (BaseObject obj : objs) {
if (obj != null) {
String subPage = obj.getStringValue(Constants.SUBASSET_CLASS_PAGE);
Map<String,Object> subInfo = new HashMap<String, Object>(11);
subInfo.put(Constants.SUBASSET_CLASS_PAGE, subPage);
subInfo.put(Constants.SUBASSET_CLASS_ORDER, obj.getLongValue(Constants.SUBASSET_CLASS_ORDER));
com.xpn.xwiki.api.XWiki xwikiApi = new com.xpn.xwiki.api.XWiki(context.getWiki(), context);
try {
Document doc = xwikiApi.getDocument(subPage);
if (doc instanceof Asset) {
subInfo = addSubinfo(subInfo, doc);
} else if (doc == null) {
// getDocument returns null if the page is not viewable by the user
subInfo = addEmptySubinfo(subInfo, ProtectedAsset.class.getSimpleName().replaceAll("Asset$", ""));
} else if (doc.isNew()) {
// Document does not exist -- thus invalid
subInfo = addEmptySubinfo(subInfo, InvalidAsset.class.getSimpleName().replaceAll("Asset$", ""));
}
} catch (Exception e) {
subInfo = addEmptySubinfo(subInfo, InvalidAsset.class.getSimpleName().replaceAll("Asset$", ""));
}
subList.add(subInfo);
}
}
Collections.sort(subList, new Comparator<Map<String,Object>>(){
public int compare(Map<String,Object> s1, Map<String,Object> s2){
return ((Long) s1.get(Constants.SUBASSET_CLASS_ORDER)).compareTo((Long) s2.get(Constants.SUBASSET_CLASS_ORDER));
}
});
return subList;
}
return new ArrayList<Map<String,Object>>(1);
}
public List<String> getSubassetList() {
List<BaseObject> objs = doc.getObjects(Constants.SUBASSET_CLASS);
if (objs != null && objs.size() > 0) {
objs = sortSubassetList(objs);
List<String> list = new ArrayList<String>();
for (BaseObject obj : objs){
if (obj != null) {
list.add(obj.getStringValue(Constants.SUBASSET_CLASS_PAGE));
}
}
return filterViewablePages(list);
}
return new ArrayList<String>();
}
protected List<BaseObject> sortSubassetList(List<BaseObject> list) {
if (list != null && list.size() > 0) {
Collections.sort(list, new Comparator<BaseObject>(){
public int compare(BaseObject s1, BaseObject s2){
if (s1 == null) {
return s2 == null ? 0 : -1;
} else if (s2 == null) {
return 1;
}
Long c1 = s1.getLongValue(Constants.SUBASSET_CLASS_ORDER);
Long c2 = s2.getLongValue(Constants.SUBASSET_CLASS_ORDER);
if (c1 == null) {
return c2 == null ? 0 : -1;
} else if (c2 == null) {
return 1;
}
return (c1.compareTo(c2));
}
});
}
return list;
}
public Map<String,Object> getSubassetInfo(long subassetId) throws AssetException {
List<BaseObject> objs = doc.getObjects(Constants.SUBASSET_CLASS);
Map<String,Object> subInfo = new HashMap<String, Object>(11);
if (objs != null) {
for (BaseObject obj : objs){
if (obj != null) {
String subPage = obj.getStringValue(Constants.SUBASSET_CLASS_PAGE);
Long order = obj.getLongValue(Constants.SUBASSET_CLASS_ORDER);
if (order.equals(subassetId)) {
subInfo.put(Constants.SUBASSET_CLASS_PAGE, subPage);
subInfo.put(Constants.SUBASSET_CLASS_ORDER, order);
com.xpn.xwiki.api.XWiki xwikiApi = new com.xpn.xwiki.api.XWiki(context.getWiki(), context);
try {
Document doc = xwikiApi.getDocument(subPage);
if (doc instanceof Asset) {
subInfo = addSubinfo(subInfo, doc);
} else {
subInfo.put("error", "Subasset does not exist");
}
} catch (Exception e) {
subInfo.put("error", "Subasset does not exist");
}
return subInfo;
}
}
}
}
throw new AssetException(AssetException.ERROR_ASSET_SUBASSET_NOTFOUND, "No subasset exists with the order number "+subassetId);
}
public long insertSubassetBefore(String page, String beforePage) throws XWikiException {
if (beforePage == null){
return addSubasset(page);
}
List<BaseObject> objs = doc.getObjects(Constants.SUBASSET_CLASS);
Long beforePosition = null;
if (objs != null) {
for (BaseObject obj : objs) {
if (obj != null) {
String objName = obj.getStringValue(Constants.SUBASSET_CLASS_PAGE);
if (objName.equals(beforePage)){
beforePosition = obj.getLongValue(Constants.SUBASSET_CLASS_ORDER);
}
}
}
}
return insertSubassetAt(page, beforePosition);
}
public long insertSubassetAt(String page, Long atPosition) throws XWikiException {
if (atPosition == null || atPosition == -1){
return addSubasset(page);
}
relocateAssets(atPosition);
createSubasset(page, atPosition);
return atPosition;
}
protected void relocateAssets(long freePosition) throws XWikiException {
List<BaseObject> objs = doc.getObjects(Constants.SUBASSET_CLASS);
if (objs == null) {
return ;
}
for (BaseObject obj : objs) {
if (obj != null) {
long objPos = obj.getLongValue(Constants.SUBASSET_CLASS_ORDER);
if (objPos >= freePosition) {
obj.setLongValue(Constants.SUBASSET_CLASS_ORDER, objPos + 1);
}
}
}
}
public long addSubasset(String page) throws XWikiException {
Long highestOrder = getLastPosition() + 1;
createSubasset(page, highestOrder);
return highestOrder;
}
public void createSubasset(String page, Long position) throws XWikiException {
com.xpn.xwiki.api.XWiki xwikiApi = new com.xpn.xwiki.api.XWiki(context.getWiki(), context);
try {
Document subAsset = xwikiApi.getDocument(page);
if (subAsset instanceof Asset) {
// Do not allow ancestor to be added as a sub-asset
boolean done = false;
List<String> searchFor = new ArrayList<String>();
searchFor.add(doc.getFullName());
while (!done){
String sql = null;
for (String item : searchFor) {
if (item.equals(page)) {
XWikiMessageTool msg = (XWikiMessageTool) context.get("msg");
throw new AssetException(AssetException.ERROR_ASSET_SUBASSET_RECURSION, msg.get("addsubasset.recursive_add_message"));
}
if (sql != null) {
sql = sql + ", '" + item + "'";
} else {
sql = "'" + item + "'";
}
}
sql = ", BaseObject as obj, StringProperty as prop where obj.name=doc.fullName and obj.className='"+Constants.SUBASSET_CLASS+"' and prop.id.id = obj.id and prop.name='"+Constants.SUBASSET_CLASS_PAGE+"' and prop.value in (" + sql + ")";
List<String> list = context.getWiki().getStore().searchDocumentsNames(sql, context);
if ((list==null)||(list.size()==0)){
done = true;
} else {
searchFor = list;
}
}
// Is not being added to itself
BaseObject obj = doc.newObject(Constants.SUBASSET_CLASS, context);
obj.setStringValue(Constants.SUBASSET_CLASS_PAGE, subAsset.getFullName());
obj.setLongValue(Constants.SUBASSET_CLASS_ORDER, position);
} else {
throw new AssetException(AssetException.ERROR_ASSET_SUBASSET_NOTFOUND, "Subasset to add does not exist");
}
} catch (Exception e) {
throw new AssetException(AssetException.ERROR_ASSET_SUBASSET_NOTFOUND, "Subasset to add does not exist");
}
}
public void setSubassets(List<String> wantedList) throws XWikiException {
XWikiDocument assetDoc = getDoc();
List<BaseObject> existingList = assetDoc.getObjects(Constants.SUBASSET_CLASS);
String[] want = new String[0];
if (wantedList != null) {
want = wantedList.toArray(want);
}
BaseObject[] existing = new BaseObject[0];
if (existingList != null) {
existing = existingList.toArray(existing);
}
// TODO: Remove
// DEBUGGING CODE for CURRIKI-4238
System.out.println("REORDER "+assetDoc.getFullName()+" want: "+(wantedList==null?"NULL":wantedList.toString()));
System.out.println("REORDER "+assetDoc.getFullName()+" existing: "+(existingList==null?"NULL":existingList.toString()));
int wSize = (wantedList != null)?want.length:0;
int eSize = (existingList != null)?existing.length:0;
int e = 0;
int w = 0;
while (w < wSize) {
if (want[w] != null && context.getWiki().exists(want[w], context)) {
// Only add the asset if it still exists
BaseObject b = null;
while (b == null && e < eSize) {
b = existing[e];
e++;
}
if (b == null) {
System.out.println("REORDER "+assetDoc.getFullName()+" Adding object w="+w+" e="+e);
b = assetDoc.newObject(Constants.SUBASSET_CLASS, context);
}
else {
System.out.println("REORDER "+assetDoc.getFullName()+" Updating object w="+w+" e="+e);
}
b.setStringValue(Constants.SUBASSET_CLASS_PAGE, want[w]);
b.setLongValue(Constants.SUBASSET_CLASS_ORDER, w);
}
w++;
}
while (e < eSize) {
BaseObject b = null;
while (b == null && e < eSize) {
b = existing[e];
e++;
}
if (b != null) {
System.out.println("REORDER "+assetDoc.getFullName()+" Removing object w="+w+" e="+e);
assetDoc.removeObject(b);
}
}
}
public void reorder(String previousRevision, List<String> want) throws XWikiException {
if (!getVersion().equals(previousRevision)){
throw new AssetException(AssetException.ERROR_ASSET_REORDER_NOTMATCH, "This resource has been updated since originally checked");
}
setSubassets(want);
}
public void reorder(List<String> orig, List<String> want) throws XWikiException {
List<String> cur = getSubassetList();
// Check that the original list matches the current list
int i = 0;
for (String page : cur){
if (!page.equals(orig.get(i))){
throw new AssetException(AssetException.ERROR_ASSET_REORDER_NOTMATCH, "Original list does not match current list");
}
++i;
}
setSubassets(want);
}
protected long getLastPosition() {
List<BaseObject> objs = doc.getObjects(Constants.SUBASSET_CLASS);
long highestOrder = (long) -1;
if (objs != null) {
for (BaseObject obj : objs) {
if (obj != null) {
long objOrder = obj.getLongValue(Constants.SUBASSET_CLASS_ORDER);
if (objOrder > highestOrder) {
highestOrder = objOrder;
}
}
}
}
return highestOrder;
}
protected List<String> filterViewablePages(List<String> pageList) {
List<String> results = new ArrayList<String>();
if (pageList!=null) {
String favorites = getSpace()+"."+ Constants.FAVORITES_COLLECTION_PAGE;
for (String page : pageList) {
try {
if (context.getWiki().getRightService().hasAccessLevel("view", context.getUser(), page, context) && context.getWiki().exists(page, context) && !favorites.equals(page)) {
results.add(page);
}
} catch (XWikiException e) {
// Ignore exception -- just don't add to result list
LOG.error("Error filtering collections", e);
}
}
}
return results;
}
protected void determineCategory() throws XWikiException {
BaseObject obj = doc.getObject(Constants.ASSET_CLASS);
if (obj != null) {
obj.setStringValue(Constants.ASSET_CLASS_CATEGORY, CATEGORY_NAME);
}
}
}