Index: core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/DocumentInfoAPI.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/DocumentInfoAPI.java	(révision 26062)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/DocumentInfoAPI.java	(copie de travail)
@@ -59,9 +59,19 @@
 
     public String getFullName()
     {
-        return (doc.getFullName());
+        return doc.getFullName();
     }
 
+    /**
+     * @return the language of the described document
+     * 
+     * @since 2.2M1
+     */
+    public String getLanguage()
+    {
+        return doc.getLanguage();
+    }
+    
     public int isInstallable()
     {
         return doc.isInstallable();
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/PackageAPI.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/PackageAPI.java	(révision 26062)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/PackageAPI.java	(copie de travail)
@@ -25,6 +25,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import net.sf.json.JSONObject;
+
 import com.xpn.xwiki.XWikiContext;
 import com.xpn.xwiki.XWikiException;
 import com.xpn.xwiki.api.Api;
@@ -34,6 +36,11 @@
 {
     Package plugin;
 
+    /**
+     * The possible strategies available to deal with versions when installing a package.
+     */
+    public enum HistoryStrategy { RESET, REPLACE, ADD_VERSION };
+    
     public PackageAPI(Package plugin, XWikiContext context) throws PackageException
     {
         super(context);
@@ -109,31 +116,86 @@
         this.plugin.setAuthorName(authorName);
     }
 
+    /**
+     * Configure the installer to preserve or not authors and dates from the associated package.
+     * 
+     * Note that the value "true" will be ignored at installation time if the context user
+     * (the one that "installs") is not a "farm admin" (i.e. has admin access level on 
+     * the master wiki of the farm), for security reasons.
+     * 
+     * @since 2.2M1
+     */
+    public void setImportAsBackup(boolean importAsBackup)
+    {
+    	this.plugin.setImportAsBackup(importAsBackup);
+    }
+    
+    /**
+     * @return the current configuration of the installer regarding preservation of authors
+     * and dates from the package.
+     * 
+     * @since 2.2M1
+     */
+    public boolean isImportAsBackup()
+    {
+    	return this.plugin.isImportAsBackup();
+    }
+    
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #isImportAsBackup()} instead
+     */
     public boolean isBackupPack()
     {
         return this.plugin.isBackupPack();
     }
 
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #setImportAsBackup(boolean)} instead
+     */
     public void setBackupPack(boolean backupPack)
     {
         this.plugin.setBackupPack(backupPack);
     }
 
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #getHistoryStrategy()} instead
+     */
     public boolean isVersionPreserved()
     {
         return this.plugin.isVersionPreserved();
     }
 
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #setHistoryStrategy(HistoryStrategy)} instead
+     */
     public void setPreserveVersion(boolean preserveVersion)
     {
         this.plugin.setPreserveVersion(preserveVersion);
     }
 
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #getHistoryStrategy()} instead
+     */
     public boolean isWithVersions()
     {
         return this.plugin.isWithVersions();
     }
 
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #setHistoryStrategy(HistoryStrategy) instead
+     */
     public void setWithVersions(boolean withVersions)
     {
         this.plugin.setWithVersions(withVersions);
@@ -194,6 +256,34 @@
         return this.plugin.export(getXWikiContext().getResponse().getOutputStream(), getXWikiContext());
     }
 
+
+    /** 
+     * Similar to {@link #Import(byte[])}, except expected errors are catch. This version should be privileged
+     * when using the packager API from velocity scripts since it will not display stack-trace in case of error
+     * (for example if the passed file is not a valid package).
+     * 
+     * @param data the file to create the package from, as a byte array.
+     * 
+     * @return true if the package creation succeeded, false otherwise. If the package creation failed, 
+     * the error message is placed in the velocity context under the <code>import_error</code> key,
+     * 
+     * @since 2.2M1
+     */
+    public boolean importPackageFromByteArray(byte data[]) {
+        try {
+            this.plugin.Import(data, getXWikiContext());
+            return true;
+        }
+        catch (XWikiException e) {
+            getXWikiContext().put("import_error", e.getMessage());
+            return false;
+        }
+        catch (IOException e) {
+            getXWikiContext().put("import_error", e.getMessage());
+            return false;            
+        }
+    }
+    
     public String Import(byte file[]) throws IOException, XWikiException
     {
         return this.plugin.Import(file, getXWikiContext());
@@ -219,6 +309,16 @@
     {
         return this.plugin.toXml(getXWikiContext());
     }
+    
+    /**
+     * @return a representation of this package under the JSON format
+     * 
+     * @since 2.2M1
+     */
+    public JSONObject toJSON()
+    {
+        return this.plugin.toJSON(getXWikiContext());
+    }
 
     public int install() throws XWikiException
     {
@@ -244,4 +344,24 @@
     {
         return this.plugin.getStatus(getXWikiContext());
     }
+    
+    /**
+     * Sets the strategy defining how to deal with history revision when importing the
+     * package with {@link #install()}
+     * 
+     * @see HistoryStrategy
+     * @param strategy the strategy to set for this installation
+     */
+    public void setHistoryStrategy(HistoryStrategy strategy)
+    {
+    	this.plugin.setHistoryStrategy(strategy);
+    }
+    
+    /**
+     * @return the currently set {@link HistoryStrategy}
+     */
+    public HistoryStrategy getHistoryStrategy()
+    {
+    	return this.plugin.getHistoryStrategy();
+    }
 }
Index: core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/Package.java
===================================================================
--- core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/Package.java	(révision 26062)
+++ core/xwiki-core/src/main/java/com/xpn/xwiki/plugin/packaging/Package.java	(copie de travail)
@@ -30,11 +30,17 @@
 import java.io.OutputStream;
 import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Date;
+import java.util.Map;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
 
+import net.sf.json.JSONObject;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.dom4j.Document;
@@ -53,6 +59,7 @@
 import com.xpn.xwiki.doc.XWikiAttachment;
 import com.xpn.xwiki.doc.XWikiDocument;
 import com.xpn.xwiki.objects.classes.BaseClass;
+import com.xpn.xwiki.plugin.packaging.PackageAPI.HistoryStrategy;
 
 public class Package
 {
@@ -67,7 +74,7 @@
     public static final String DefaultPluginName = "package";
 
     private static final Log LOG = LogFactory.getLog(Package.class);
-
+    
     private String name = "My package";
 
     private String description = "";
@@ -84,14 +91,84 @@
 
     private List<DocumentInfo> classFiles = null;
 
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #importAsBackup} instead
+     */
     private boolean backupPack = false;
 
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #historyStrategy} instead
+     */
     private boolean preserveVersion = false;
 
+    /**
+     * @deprecated since 2.2M1
+     * 
+     * Use {@link #historyStrategy} instead
+     */
     private boolean withVersions = true;
+    
+    /**
+     * The chosen strategy to deal with history revisions when installing 
+     * this package with {@link #install(XWikiContext)}
+     */
+    private HistoryStrategy historyStrategy;
+    
+    /**
+     * @return the currently set {@link Package.HistoryStrategy} for this package
+     * 
+     * @since 2.2M1
+     */
+    public HistoryStrategy getHistoryStrategy()
+    {
+		return historyStrategy;
+	}
 
-    private List<DocumentFilter> documentFilters = new ArrayList<DocumentFilter>();
+    /**
+     * Sets the strategy to handle versions of document for this package
+     * 
+     * @param historyStrategy the strategy to use
+     * @since 2.2M1
+     */
+	public void setHistoryStrategy(HistoryStrategy historyStrategy) 
+	{
+		this.historyStrategy = historyStrategy;
+	}
 
+	/**
+     * Should the installation preserve authors and dates from the package.
+     * This option is available only to farm admins (It will be ignored if set to true
+     * by a user that has less than farm admin access level).
+     */
+    private boolean importAsBackup = false;
+    
+    /**
+     * @return true if the installation is set to preserve authors and dates on import 
+     * @since 2.2M1
+     */
+    public boolean isImportAsBackup() 
+    {
+		return importAsBackup;
+	}
+
+    /**
+     * Set the strategy the installation should use for authorship of imported document.
+     * 
+     * @param importAsBackup should or not the installation of documents preserve authors
+     * and dates from the package.
+     * @since 2.2M1
+     */
+	public void setImportAsBackup(boolean importAsBackup) 
+	{
+		this.importAsBackup = importAsBackup;
+	}
+
+	private List<DocumentFilter> documentFilters = new ArrayList<DocumentFilter>();
+
     public String getName()
     {
         return this.name;
@@ -146,31 +223,41 @@
      * If true, the package will preserve the original author during import, rather than updating the author to the
      * current (importing) user.
      * 
-     * @see #isWithVersions()
+     * @see #isWithVersions() 
      * @see #isVersionPreserved()
+     * 
+     * @deprecated since 2.2M1. Use {@link #isImportAsBackup()} instead
      */
     public boolean isBackupPack()
     {
         return this.backupPack;
     }
 
+    /**
+     * @deprecated since 2.2M1. Use {@link #setImportAsBackup(boolean)} instead
+     */
     public void setBackupPack(boolean backupPack)
     {
         this.backupPack = backupPack;
     }
-
+    
     /**
      * If true, the package will preserve the current document version during import, regardless of whether or not the
      * document history is included.
      * 
      * @see #isWithVersions()
      * @see #isBackupPack()
+     * 
+     * @deprecated since 2.2M1. Use {@link #getHistoryStrategy()} instead
      */
     public boolean isVersionPreserved()
     {
         return this.preserveVersion;
     }
 
+    /**
+     * @deprecated since 2.2M1. Use {@link #getHistoryStrategy()} instead
+     */
     public void setPreserveVersion(boolean preserveVersion)
     {
         this.preserveVersion = preserveVersion;
@@ -186,6 +273,9 @@
         return this.customMappingFiles;
     }
 
+    /**
+     * @deprecated since 2.2M1. Use {@link #getHistoryStrategy()} instead.
+     */
     public boolean isWithVersions()
     {
         return this.withVersions;
@@ -196,6 +286,8 @@
      * that the old version is preserved.
      * 
      * @see #isVersionPreserved(boolean)
+     * 
+     * @deprecated since 2.2M1. Use {@link #getHistoryStrategy()} instead
      */
     public void setWithVersions(boolean withVersions)
     {
@@ -506,6 +598,21 @@
     {
         boolean isAdmin = context.getWiki().getRightService().hasAdminRights(context);
 
+        if (this.historyStrategy == null) {
+        	// Compatibility code to handle consumers of this plugin that do not use the
+        	// versionStrategy (introduced in 2.2M1)
+        	// We set here the versionStrategy based on former parameters (now deprecated)
+        	if (this.withVersions && this.preserveVersion) {
+        		this.historyStrategy = HistoryStrategy.REPLACE;
+        	}
+        	else if (this.preserveVersion) {
+        		this.historyStrategy = HistoryStrategy.RESET;
+        	}
+        	else {
+        		this.historyStrategy = HistoryStrategy.ADD_VERSION;
+        	}
+        }
+        
         if (testInstall(isAdmin, context) == DocumentInfo.INSTALL_IMPOSSIBLE) {
             setStatus(DocumentInfo.INSTALL_IMPOSSIBLE, context);
             return DocumentInfo.INSTALL_IMPOSSIBLE;
@@ -543,7 +650,7 @@
 
         return status;
     }
-
+    
     private int installDocument(DocumentInfo doc, boolean isAdmin, XWikiContext context) throws XWikiException
     {
         int result = DocumentInfo.INSTALL_OK;
@@ -564,41 +671,70 @@
         }
         if (status == DocumentInfo.INSTALL_OK || status == DocumentInfo.INSTALL_ALREADY_EXIST
             && doc.getAction() == DocumentInfo.ACTION_OVERWRITE) {
+            XWikiDocument previousdoc = null;
             if (status == DocumentInfo.INSTALL_ALREADY_EXIST) {
-                XWikiDocument deleteddoc = context.getWiki().getDocument(doc.getFullName(), context);
+                previousdoc = context.getWiki().getDocument(doc.getFullName(), context);
                 // if this document is a translation: we should only delete the translation
                 if (doc.getDoc().getTranslation() != 0) {
-                    deleteddoc = deleteddoc.getTranslatedDocument(doc.getLanguage(), context);
+                    previousdoc = previousdoc.getTranslatedDocument(doc.getLanguage(), context);
                 }
-                try {
-                    // This is not a real document delete, it's a upgrade. To be sure to not
-                    // generate DELETE notification we directly use {@link XWikiStoreInterface}
-                    context.getWiki().getStore().deleteXWikiDoc(deleteddoc, context);
-                } catch (Exception e) {
-                    // let's log the error but not stop
-                    result = DocumentInfo.INSTALL_ERROR;
-                    addToErrors(doc.getFullName() + ":" + doc.getLanguage(), context);
-                    if (LOG.isErrorEnabled()) {
-                        LOG.error("Failed to delete document " + deleteddoc.getFullName());
+                // we should only delete the previous document
+                // if we are overridding the versions and/or if this is a backup pack
+                if (!this.getHistoryStrategy().equals(PackageAPI.HistoryStrategy.ADD_VERSION)) {
+                    try {
+                        // This is not a real document delete, it's a upgrade. To be sure to not
+                        // generate DELETE notification we directly use {@link XWikiStoreInterface}
+                        context.getWiki().getStore().deleteXWikiDoc(previousdoc, context);
+                    } catch (Exception e) {
+                        // let's log the error but not stop
+                        result = DocumentInfo.INSTALL_ERROR;
+                        addToErrors(doc.getFullName() + ":" + doc.getLanguage(), context);
+                        if (LOG.isErrorEnabled()) {
+                            LOG.error("Failed to delete document " + previousdoc.getFullName());
+                        }
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("Failed to delete document " + previousdoc.getFullName(), e);
+                        }
                     }
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug("Failed to delete document " + deleteddoc.getFullName(), e);
-                    }
                 }
             }
             try {
-                if (!this.backupPack) {
+            	// Determine if the user performing the installation is a farm admin.
+            	// Right now, check for programming rights, which is the closer we can get to the notion
+            	// of farm admin.
+            	boolean isFarmAdmin = context.getWiki().getRightService().hasProgrammingRights(context);
+            	
+                if (!this.importAsBackup || !isFarmAdmin) {
+                	// We allow author preservation from the package only to farm admins,
+                	// In order to prevent sub-wiki admins to take control of a farm with forged packages
                     doc.getDoc().setAuthor(context.getUser());
+                    doc.getDoc().setContentAuthor(context.getUser());
+                    // if the import is not a backup pack we set the date to now
+                    Date date = new Date();
+                    doc.getDoc().setDate(date);
+                    doc.getDoc().setContentUpdateDate(date);
                 }
 
-                if ((!this.preserveVersion) && (!this.withVersions)) {
-                    doc.getDoc().setVersion("1.1");
+                if (this.historyStrategy.equals(HistoryStrategy.ADD_VERSION) && previousdoc != null) {                	
+                    // If it is not a backup pack and we are not overriding the versions
+                    // then we want to insert the archive from the original doc
+                    doc.getDoc().setDocumentArchive(previousdoc.getDocumentArchive(context));
+                } 
+                
+                else {
+                	// Reset or replace history
+                	// if there was not history in the source package then we should reset the version number to 1.1
+                	if (!this.documentContainsHistory(doc) || this.historyStrategy.equals(HistoryStrategy.RESET)) {
+                		doc.getDoc().setVersion("1.1");
+                	}
+
+                	// We don't want date and version to change
+                	// So we need to cancel the dirty status
+                	doc.getDoc().setContentDirty(false);
+                	doc.getDoc().setMetaDataDirty(false);
                 }
 
-                // We don't want date and version to change
-                // So we need to cancel the dirty status
-                doc.getDoc().setContentDirty(false);
-                doc.getDoc().setMetaDataDirty(false);
+                // Attachment saving should not generate additional saving
                 for (XWikiAttachment xa : doc.getDoc().getAttachmentList()) {
                     xa.setMetaDataDirty(false);
                     xa.getAttachment_content().setContentDirty(false);
@@ -608,18 +744,18 @@
                 doc.getDoc().saveAllAttachments(false, true, context);
                 addToInstalled(doc.getFullName() + ":" + doc.getLanguage(), context);
 
-                if (this.withVersions) {
+                if (this.historyStrategy.equals(HistoryStrategy.REPLACE)
+                     || this.historyStrategy.equals(HistoryStrategy.ADD_VERSION)) {
                     // we need to force the saving the document archive.
                     if (doc.getDoc().getDocumentArchive() != null) {
                         context.getWiki().getVersioningStore().saveXWikiDocArchive(
                             doc.getDoc().getDocumentArchive(context), true, context);
                     }
                 }
+
                 // if there is no archive in xml and content&metaData Dirty is not set
-                // then archive was not saved
-                // so we need save it via resetArchive
-                if ((doc.getDoc().getDocumentArchive() == null)
-                    || (doc.getDoc().getDocumentArchive().getNodes() == null) || (!this.withVersions)) {
+                // then archive was not saved so we need save it via resetArchive
+                if (!this.documentContainsHistory(doc)) {	
                     doc.getDoc().resetArchive(context);
                 }
             } catch (XWikiException e) {
@@ -636,6 +772,20 @@
         return result;
     }
 
+    /**
+     * @return true if the passed document contains a (not-empty) history of previous versions, false otherwise
+     */
+    private boolean documentContainsHistory(DocumentInfo doc)
+    {
+    	if ((doc.getDoc().getDocumentArchive() == null)
+                || (doc.getDoc().getDocumentArchive().getNodes() == null)
+                || (doc.getDoc().getDocumentArchive().getNodes().size() == 0)) {
+            return false;
+        }
+    	return true;
+    }
+    
+    
     private List<String> getStringList(String name, XWikiContext context)
     {
         List<String> list = (List<String>) context.get(name);
@@ -979,7 +1129,7 @@
         this.version = getElementText(infosEl, "version");
         this.backupPack = new Boolean(getElementText(infosEl, "backupPack")).booleanValue();
         this.preserveVersion = new Boolean(getElementText(infosEl, "preserveVersion")).booleanValue();
-
+        
         return domdoc;
     }
 
@@ -1106,4 +1256,56 @@
 
         return "";
     }
+
+    /**
+     * Outputs the content of this package in the JSON format
+     * 
+     * @param wikiContext the XWiki context
+     * @return a representation of this package under the JSON format
+     * 
+     * @since 2.2M1
+     */
+    public JSONObject toJSON(XWikiContext wikiContext)
+    {
+        Map<String, Object> json = new HashMap<String, Object>();
+
+        Map<String, Object> infos = new HashMap<String, Object>();
+        infos.put("name", this.name);
+        infos.put("description", this.description);
+        infos.put("licence", this.licence);
+        infos.put("author", this.authorName);
+        infos.put("version", this.version);
+
+        Map<String, Map<String, List<Map<String, String>>>> files =
+            new LinkedHashMap<String, Map<String, List<Map<String, String>>>>();
+
+        for (DocumentInfo docInfo : this.files) {
+            Map<String, String> fileInfos = new HashMap<String, String>();
+            fileInfos.put("defaultAction", String.valueOf(docInfo.getAction()));
+            fileInfos.put("language", String.valueOf(docInfo.getLanguage()));
+            fileInfos.put("fullName", docInfo.getFullName());
+
+            // If the space does not exist in the map of spaces, we create it.
+            if (files.get(docInfo.getDoc().getSpace()) == null) {
+                files.put(docInfo.getDoc().getSpace(), new LinkedHashMap<String, List<Map<String, String>>>());
+            }
+            
+            // If the document name does not exists in the space map of docs, we create it.
+            if (files.get(docInfo.getDoc().getSpace()).get(docInfo.getDoc().getName()) == null) {
+                files.get(docInfo.getDoc().getSpace()).put(docInfo.getDoc().getName(),
+                    new ArrayList<Map<String, String>>());
+            }
+            
+            // Finally we add the file infos (language, fullname and action) to the list of translations
+            // for that document.
+            files.get(docInfo.getDoc().getSpace()).get(docInfo.getDoc().getName()).add(fileInfos);
+        }
+
+        json.put("infos", infos);
+        json.put("files", files);
+        
+        JSONObject jsonObject = JSONObject.fromObject(json);
+        
+        return jsonObject;
+    }
 }
