diff --git a/migration/i7.1/oracle/201608191500_Ticket_1006528.sql b/migration/i7.1/oracle/201608191500_Ticket_1006528.sql new file mode 100644 index 0000000000..da3e3479a4 --- /dev/null +++ b/migration/i7.1/oracle/201608191500_Ticket_1006528.sql @@ -0,0 +1,43 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- Aug 18, 2016 9:03:49 PM MYT +-- 1006528 Implement Image Storage Provider +INSERT INTO AD_Element (AD_Element_ID,ColumnName,Updated,Name,Description,PrintName,AD_Element_UU,IsActive,Created,AD_Org_ID,CreatedBy,UpdatedBy,AD_Client_ID,EntityType) VALUES (203038,'StorageImage_ID',TO_DATE('2016-08-18 21:03:42','YYYY-MM-DD HH24:MI:SS'),'Image Store','Storage provider for Image','Image Store','a4df3881-4d7e-4b3b-a71a-3380bdebf371','Y',TO_DATE('2016-08-18 21:03:42','YYYY-MM-DD HH24:MI:SS'),0,100,100,0,'D') +; + +-- Aug 18, 2016 9:05:23 PM MYT +INSERT INTO AD_Column (AD_Column_ID,SeqNoSelection,IsSyncDatabase,Version,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,Updated,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,IsActive,CreatedBy,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,AD_Org_ID,Created,EntityType,IsEncrypted,IsSecure,FKConstraintType,AD_Element_ID,AD_Reference_ID,AD_Reference_Value_ID,AD_Table_ID) VALUES (212831,0,'N',0,'N','N','N',0,'N',22,'N','N','N','Y','a3d7a4ea-9d2e-4da6-b48f-7ee5242719ee',TO_DATE('2016-08-18 21:05:07','YYYY-MM-DD HH24:MI:SS'),'Y','StorageImage_ID','Storage provider for Image','Image Store','Y','Y',100,100,'N','N',0,0,TO_DATE('2016-08-18 21:05:07','YYYY-MM-DD HH24:MI:SS'),'D','N','N','N',203038,18,200023,227) +; + +-- Aug 18, 2016 9:05:59 PM MYT +UPDATE AD_Column SET FKConstraintType='N', FKConstraintName='StorageImage_ADClientInfo',Updated=TO_DATE('2016-08-18 21:05:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=212831 +; + +-- Aug 18, 2016 9:05:59 PM MYT +ALTER TABLE AD_ClientInfo ADD StorageImage_ID NUMBER(10) DEFAULT NULL +; + +-- Aug 18, 2016 9:06:00 PM MYT +ALTER TABLE AD_ClientInfo ADD CONSTRAINT StorageImage_ADClientInfo FOREIGN KEY (StorageImage_ID) REFERENCES ad_storageprovider(ad_storageprovider_id) DEFERRABLE INITIALLY DEFERRED +; + +-- Aug 18, 2016 9:07:51 PM MYT +INSERT INTO AD_Field (SortNo,AD_Field_ID,IsEncrypted,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,IsReadOnly,AD_Org_ID,Updated,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,CreatedBy,UpdatedBy,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,Created,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,EntityType,AD_Tab_ID) VALUES (0,204272,'N',0,'N','N',1020,'Y','N',0,TO_DATE('2016-08-18 21:07:45','YYYY-MM-DD HH24:MI:SS'),'Storage provider for Image','Image Store','471cc623-0460-4b6a-8e24-74055858ce8a','Y','N',100,100,'Y','Y',1020,1,'N',0,TO_DATE('2016-08-18 21:07:45','YYYY-MM-DD HH24:MI:SS'),2,1,'N','N',212831,'D',169) +; + +-- Aug 18, 2016 9:08:51 PM MYT +UPDATE AD_Field SET SeqNo=120, IsDisplayed='Y', XPosition=1,Updated=TO_DATE('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204272 +; + +-- Aug 18, 2016 9:08:51 PM MYT +UPDATE AD_Field SET SeqNo=125,Updated=TO_DATE('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202532 +; + +-- Aug 18, 2016 9:08:51 PM MYT +UPDATE AD_Field SET SeqNo=130,Updated=TO_DATE('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202533 +; + +SELECT register_migration_script('201608191500_Ticket_1006528.sql') FROM dual +; + diff --git a/migration/i7.1/postgresql/201608191500_Ticket_1006528.sql b/migration/i7.1/postgresql/201608191500_Ticket_1006528.sql new file mode 100644 index 0000000000..09b933e5f0 --- /dev/null +++ b/migration/i7.1/postgresql/201608191500_Ticket_1006528.sql @@ -0,0 +1,40 @@ +-- Aug 18, 2016 9:03:49 PM MYT +-- 1006528 Implement Image Storage Provider +INSERT INTO AD_Element (AD_Element_ID,ColumnName,Updated,Name,Description,PrintName,AD_Element_UU,IsActive,Created,AD_Org_ID,CreatedBy,UpdatedBy,AD_Client_ID,EntityType) VALUES (203038,'StorageImage_ID',TO_TIMESTAMP('2016-08-18 21:03:42','YYYY-MM-DD HH24:MI:SS'),'Image Store','Storage provider for Image','Image Store','a4df3881-4d7e-4b3b-a71a-3380bdebf371','Y',TO_TIMESTAMP('2016-08-18 21:03:42','YYYY-MM-DD HH24:MI:SS'),0,100,100,0,'D') +; + +-- Aug 18, 2016 9:05:23 PM MYT +INSERT INTO AD_Column (AD_Column_ID,SeqNoSelection,IsSyncDatabase,Version,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,Updated,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,IsActive,CreatedBy,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,AD_Org_ID,Created,EntityType,IsEncrypted,IsSecure,FKConstraintType,AD_Element_ID,AD_Reference_ID,AD_Reference_Value_ID,AD_Table_ID) VALUES (212831,0,'N',0,'N','N','N',0,'N',22,'N','N','N','Y','a3d7a4ea-9d2e-4da6-b48f-7ee5242719ee',TO_TIMESTAMP('2016-08-18 21:05:07','YYYY-MM-DD HH24:MI:SS'),'Y','StorageImage_ID','Storage provider for Image','Image Store','Y','Y',100,100,'N','N',0,0,TO_TIMESTAMP('2016-08-18 21:05:07','YYYY-MM-DD HH24:MI:SS'),'D','N','N','N',203038,18,200023,227) +; + +-- Aug 18, 2016 9:05:59 PM MYT +UPDATE AD_Column SET FKConstraintType='N', FKConstraintName='StorageImage_ADClientInfo',Updated=TO_TIMESTAMP('2016-08-18 21:05:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=212831 +; + +-- Aug 18, 2016 9:05:59 PM MYT +ALTER TABLE AD_ClientInfo ADD COLUMN StorageImage_ID NUMERIC(10) DEFAULT NULL +; + +-- Aug 18, 2016 9:06:00 PM MYT +ALTER TABLE AD_ClientInfo ADD CONSTRAINT StorageImage_ADClientInfo FOREIGN KEY (StorageImage_ID) REFERENCES ad_storageprovider(ad_storageprovider_id) DEFERRABLE INITIALLY DEFERRED +; + +-- Aug 18, 2016 9:07:51 PM MYT +INSERT INTO AD_Field (SortNo,AD_Field_ID,IsEncrypted,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,IsReadOnly,AD_Org_ID,Updated,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,CreatedBy,UpdatedBy,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,Created,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,EntityType,AD_Tab_ID) VALUES (0,204272,'N',0,'N','N',1020,'Y','N',0,TO_TIMESTAMP('2016-08-18 21:07:45','YYYY-MM-DD HH24:MI:SS'),'Storage provider for Image','Image Store','471cc623-0460-4b6a-8e24-74055858ce8a','Y','N',100,100,'Y','Y',1020,1,'N',0,TO_TIMESTAMP('2016-08-18 21:07:45','YYYY-MM-DD HH24:MI:SS'),2,1,'N','N',212831,'D',169) +; + +-- Aug 18, 2016 9:08:51 PM MYT +UPDATE AD_Field SET SeqNo=120, IsDisplayed='Y', XPosition=1,Updated=TO_TIMESTAMP('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204272 +; + +-- Aug 18, 2016 9:08:51 PM MYT +UPDATE AD_Field SET SeqNo=125,Updated=TO_TIMESTAMP('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202532 +; + +-- Aug 18, 2016 9:08:51 PM MYT +UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2016-08-18 21:08:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202533 +; + +SELECT register_migration_script('201608191500_Ticket_1006528.sql') FROM dual +; + diff --git a/org.adempiere.base.process/src/org/compiere/process/InOutGenerate.java b/org.adempiere.base.process/src/org/compiere/process/InOutGenerate.java index c3dba63b85..557f06c911 100644 --- a/org.adempiere.base.process/src/org/compiere/process/InOutGenerate.java +++ b/org.adempiere.base.process/src/org/compiere/process/InOutGenerate.java @@ -298,6 +298,10 @@ public class InOutGenerate extends SvrProcess createLine (order, line, toDeliver, null, false); continue; } + if (product == null) + { + continue; + } // Stored Product String MMPolicy = product.getMMPolicy(); diff --git a/org.adempiere.base/OSGI-INF/imagedb.xml b/org.adempiere.base/OSGI-INF/imagedb.xml new file mode 100644 index 0000000000..19499d68c7 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/imagedb.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.adempiere.base/OSGI-INF/imagefile.xml b/org.adempiere.base/OSGI-INF/imagefile.xml new file mode 100644 index 0000000000..b4ebf69169 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/imagefile.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.adempiere.base/src/org/compiere/model/ArchiveDB.java b/org.adempiere.base/src/org/compiere/model/ArchiveDB.java index 9db0d3d30a..29dcb97c4d 100644 --- a/org.adempiere.base/src/org/compiere/model/ArchiveDB.java +++ b/org.adempiere.base/src/org/compiere/model/ArchiveDB.java @@ -113,4 +113,13 @@ public class ArchiveDB implements IArchiveStore { return true; } + @Override + public boolean isPendingFlush() { + return false; + } + + @Override + public void flush(MArchive archive, MStorageProvider prov) { + } + } diff --git a/org.adempiere.base/src/org/compiere/model/ArchiveFileSystem.java b/org.adempiere.base/src/org/compiere/model/ArchiveFileSystem.java index ca2e3f978b..94ed800075 100644 --- a/org.adempiere.base/src/org/compiere/model/ArchiveFileSystem.java +++ b/org.adempiere.base/src/org/compiere/model/ArchiveFileSystem.java @@ -53,6 +53,8 @@ public class ArchiveFileSystem implements IArchiveStore { private static final CLogger log = CLogger.getCLogger(ArchiveFileSystem.class); + //temporary buffer when AD_Archive_ID=0; + private byte[] buffer; /* (non-Javadoc) * @see org.compiere.model.IArchiveStore#loadLOBData(org.compiere.model.MArchive, org.compiere.model.MStorageProvider) @@ -63,6 +65,7 @@ public class ArchiveFileSystem implements IArchiveStore { if ("".equals(archivePathRoot)) { throw new IllegalArgumentException("no attachmentPath defined"); } + buffer = null; byte[] data = archive.getByteData(); if (data == null) { return null; @@ -144,24 +147,29 @@ public class ArchiveFileSystem implements IArchiveStore { * @see org.compiere.model.IArchiveStore#save(org.compiere.model.MArchive, org.compiere.model.MStorageProvider) */ @Override - public void save(MArchive archive, MStorageProvider prov,byte[] inflatedData) { - String archivePathRoot = getArchivePathRoot(prov); - if ("".equals(archivePathRoot)) { - throw new IllegalArgumentException("no attachmentPath defined"); - } + public void save(MArchive archive, MStorageProvider prov,byte[] inflatedData) { if (inflatedData == null || inflatedData.length == 0) { throw new IllegalArgumentException("InflatedData is NULL"); } if(archive.get_ID()==0){ //set binary data otherwise save will fail archive.setByteData(new byte[]{'0'}); - if(!archive.save()) { - throw new IllegalArgumentException("unable to save MArchive"); - } + buffer = inflatedData; + } else { + write(archive, prov, inflatedData); } - final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + } + + private void write(MArchive archive, MStorageProvider prov, + byte[] inflatedData) { BufferedOutputStream out = null; try { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + String archivePathRoot = getArchivePathRoot(prov); + if ("".equals(archivePathRoot)) { + throw new IllegalArgumentException("no attachmentPath defined"); + } // create destination folder StringBuilder msgfile = new StringBuilder().append(archivePathRoot) .append(archive.getArchivePathSnippet()); @@ -203,6 +211,7 @@ public class ArchiveFileSystem implements IArchiveStore { } catch (Exception e) { log.log(Level.SEVERE, "saveLOBData", e); archive.setByteData(null); + throw new RuntimeException(e); } finally { if(out != null){ try { @@ -210,7 +219,6 @@ public class ArchiveFileSystem implements IArchiveStore { } catch (Exception e) { } } } - } private String getArchivePathRoot(MStorageProvider prov) { @@ -233,7 +241,7 @@ public class ArchiveFileSystem implements IArchiveStore { throw new IllegalArgumentException("no attachmentPath defined"); } StringBuilder msgfile = new StringBuilder().append(archivePathRoot) - .append(archive.getArchivePathSnippet()).append(archive.get_ID()).append(".pdf"); + .append(archive.getArchivePathSnippet()).append(archive.getAD_Archive_ID()).append(".pdf"); File file=new File(msgfile.toString()); if (file !=null && file.exists()) { @@ -245,4 +253,17 @@ public class ArchiveFileSystem implements IArchiveStore { return true; } + @Override + public boolean isPendingFlush() { + return buffer != null && buffer.length > 0; + } + + @Override + public void flush(MArchive archive, MStorageProvider prov) { + if (buffer != null && buffer.length > 0) { + write(archive, prov, buffer); + buffer = null; + } + } + } diff --git a/org.adempiere.base/src/org/compiere/model/AttachmentFileSystem.java b/org.adempiere.base/src/org/compiere/model/AttachmentFileSystem.java index 009c4a7df2..b50404260f 100644 --- a/org.adempiere.base/src/org/compiere/model/AttachmentFileSystem.java +++ b/org.adempiere.base/src/org/compiere/model/AttachmentFileSystem.java @@ -288,7 +288,8 @@ public class AttachmentFileSystem implements IAttachmentStore { } } attach.m_items.remove(index); - attach.saveEx(); // must save here as the operation cannot be rolled back on filesystem + if (attach.get_ID() > 0) // the attachment has not been deleted + attach.saveEx(); // must save here as the operation cannot be rolled back on filesystem if (log.isLoggable(Level.CONFIG)) log.config("Index=" + index + " - NewSize=" + attach.m_items.size()); return true; } diff --git a/org.adempiere.base/src/org/compiere/model/IArchiveStore.java b/org.adempiere.base/src/org/compiere/model/IArchiveStore.java index 7a6a598be2..f766168694 100644 --- a/org.adempiere.base/src/org/compiere/model/IArchiveStore.java +++ b/org.adempiere.base/src/org/compiere/model/IArchiveStore.java @@ -21,4 +21,7 @@ public interface IArchiveStore { public boolean deleteArchive(MArchive archive, MStorageProvider prov); + public boolean isPendingFlush(); + + public void flush(MArchive archive,MStorageProvider prov); } diff --git a/org.adempiere.base/src/org/compiere/model/IImageStore.java b/org.adempiere.base/src/org/compiere/model/IImageStore.java new file mode 100644 index 0000000000..bed0e409af --- /dev/null +++ b/org.adempiere.base/src/org/compiere/model/IImageStore.java @@ -0,0 +1,27 @@ +/****************************************************************************** + * Product: iDempiere ERP & CRM Smart Business Solution * + * Copyright (C) 2012 Trek Global * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program 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 General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ +package org.compiere.model; + +public interface IImageStore { + + public byte[] load(MImage image, MStorageProvider prov); + + public void save(MImage image, MStorageProvider prov, byte[] inflatedData); + + public boolean delete(MImage image, MStorageProvider prov); + + public boolean isPendingFlush(); + + public void flush(MImage image,MStorageProvider prov); +} diff --git a/org.adempiere.base/src/org/compiere/model/I_AD_ClientInfo.java b/org.adempiere.base/src/org/compiere/model/I_AD_ClientInfo.java index 716e523afe..a9a7e5a4ea 100644 --- a/org.adempiere.base/src/org/compiere/model/I_AD_ClientInfo.java +++ b/org.adempiere.base/src/org/compiere/model/I_AD_ClientInfo.java @@ -449,6 +449,21 @@ public interface I_AD_ClientInfo public org.compiere.model.I_AD_StorageProvider getStorageArchive() throws RuntimeException; + /** Column name StorageImage_ID */ + public static final String COLUMNNAME_StorageImage_ID = "StorageImage_ID"; + + /** Set Image Store. + * Storage provider for Image + */ + public void setStorageImage_ID (int StorageImage_ID); + + /** Get Image Store. + * Storage provider for Image + */ + public int getStorageImage_ID(); + + public org.compiere.model.I_AD_StorageProvider getStorageImage() throws RuntimeException; + /** Column name Updated */ public static final String COLUMNNAME_Updated = "Updated"; diff --git a/org.adempiere.base/src/org/compiere/model/ImageDBStorageImpl.java b/org.adempiere.base/src/org/compiere/model/ImageDBStorageImpl.java new file mode 100644 index 0000000000..95809ac62d --- /dev/null +++ b/org.adempiere.base/src/org/compiere/model/ImageDBStorageImpl.java @@ -0,0 +1,133 @@ +/****************************************************************************** + * Product: iDempiere ERP & CRM Smart Business Solution * + * Copyright (C) 2012 Trek Global * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program 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 General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ +package org.compiere.model; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.logging.Level; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import org.compiere.util.CLogger; + +/** + * @author hengsin + * + */ +public class ImageDBStorageImpl implements IImageStore { + + + private final CLogger log = CLogger.getCLogger(getClass()); + + @Override + public byte[] load(MImage image, MStorageProvider prov) { + byte[] deflatedData = image.getByteData(); + if (deflatedData == null) + return null; + // + if (log.isLoggable(Level.FINE)) log.fine("ZipSize=" + deflatedData.length); + if (deflatedData.length == 0) + return null; + + byte[] inflatedData = null; + try { + ZipInputStream zip = null; + ZipEntry entry = null; + if (MSysConfig.getBooleanValue(MSysConfig.IMAGE_DB_STORAGE_SAVE_AS_ZIP, false)) { + ByteArrayInputStream in = new ByteArrayInputStream(deflatedData); + zip = new ZipInputStream(in); + entry = zip.getNextEntry(); + } + if (entry != null) + { + // just one entry per zip + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buffer = new byte[2048]; + int length = zip.read(buffer); + while (length != -1) { + out.write(buffer, 0, length); + length = zip.read(buffer); + } + // + inflatedData = out.toByteArray(); + if (log.isLoggable(Level.FINE)) log.fine("Size=" + inflatedData.length + " - zip=" + entry.getCompressedSize() + + "(" + entry.getSize() + ") " + + (entry.getCompressedSize() * 100 / entry.getSize()) + "%"); + } else { + //not zip stream, legacy data, or saving as raw + inflatedData = deflatedData; + } + } catch (Exception e) { + log.log(Level.SEVERE, "", e); + inflatedData = null; + } + return inflatedData; + } + + @Override + public void save(MImage image, MStorageProvider prov, byte[] inflatedData) { + if (inflatedData == null || inflatedData.length == 0) { + image.setByteData(null); + return; + } + byte[] deflatedData = null; + if (MSysConfig.getBooleanValue(MSysConfig.IMAGE_DB_STORAGE_SAVE_AS_ZIP, false)) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(out); + zip.setMethod(ZipOutputStream.DEFLATED); + zip.setLevel(Deflater.BEST_COMPRESSION); + zip.setComment("idempiere"); + // + try { + ZipEntry entry = new ZipEntry(image.getName()); + entry.setTime(System.currentTimeMillis()); + entry.setMethod(ZipEntry.DEFLATED); + zip.putNextEntry(entry); + zip.write(inflatedData, 0, inflatedData.length); + zip.closeEntry(); + if (log.isLoggable(Level.FINE)) log.fine(entry.getCompressedSize() + " (" + entry.getSize() + ") " + + (entry.getCompressedSize() * 100 / entry.getSize()) + "%"); + // + // zip.finish(); + zip.close(); + deflatedData = out.toByteArray(); + if (log.isLoggable(Level.FINE)) log.fine("Length=" + inflatedData.length); + } catch (Exception e) { + log.log(Level.SEVERE, "saveLOBData", e); + deflatedData = null; + } + } else { + deflatedData = inflatedData; + } + image.setByteData(deflatedData); + } + + @Override + public boolean delete(MImage image, MStorageProvider prov) { + + return true; + } + + @Override + public boolean isPendingFlush() { + return false; + } + + @Override + public void flush(MImage image, MStorageProvider prov) { + } + +} diff --git a/org.adempiere.base/src/org/compiere/model/ImageFileStorageImpl.java b/org.adempiere.base/src/org/compiere/model/ImageFileStorageImpl.java new file mode 100644 index 0000000000..3d3178f615 --- /dev/null +++ b/org.adempiere.base/src/org/compiere/model/ImageFileStorageImpl.java @@ -0,0 +1,267 @@ +/****************************************************************************** + * Product: iDempiere ERP & CRM Smart Business Solution * + * Copyright (C) 2012 Trek Global * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program 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 General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ + +package org.compiere.model; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.logging.Level; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Source; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.Result; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; + +import org.compiere.util.CLogger; +import org.compiere.util.Util; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; +import org.w3c.dom.Element; + +/** + * @author hengsin + * + */ +public class ImageFileStorageImpl implements IImageStore { + + private String IMAGE_FOLDER_PLACEHOLDER = "%IMAGE_FOLDER%"; + + private final CLogger log = CLogger.getCLogger(getClass()); + + //temporary buffer when AD_Image_ID=0 + private byte[] buffer = null; + + @Override + public byte[] load(MImage image, MStorageProvider prov) { + String imagePathRoot = getImagePathRoot(prov); + if ("".equals(imagePathRoot)) { + throw new IllegalArgumentException("no path defined"); + } + buffer = null; + byte[] data = image.getByteData(); + if (data == null) { + return null; + } + + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + try { + final DocumentBuilder builder = factory.newDocumentBuilder(); + final Document document = builder.parse(new ByteArrayInputStream(data)); + final NodeList entries = document.getElementsByTagName("entry"); + if(entries.getLength()!=1){ + log.severe("no image entry found"); + } + final Node entryNode = entries.item(0); + final NamedNodeMap attributes = entryNode.getAttributes(); + final Node fileNode = attributes.getNamedItem("file"); + if(fileNode==null ){ + log.severe("no filename for entry"); + return null; + } + String filePath = fileNode.getNodeValue(); + if (log.isLoggable(Level.FINE)) log.fine("filePath: " + filePath); + if(filePath!=null){ + filePath = filePath.replaceFirst(IMAGE_FOLDER_PLACEHOLDER, imagePathRoot.replaceAll("\\\\","\\\\\\\\")); + //just to be shure... + String replaceSeparator = File.separator; + if(!replaceSeparator.equals("/")){ + replaceSeparator = "\\\\"; + } + filePath = filePath.replaceAll("/", replaceSeparator); + filePath = filePath.replaceAll("\\\\", replaceSeparator); + } + if (log.isLoggable(Level.FINE)) log.fine("filePath: " + filePath); + final File file = new File(filePath); + if (file.exists()) { + // read files into byte[] + final byte[] dataEntry = new byte[(int) file.length()]; + try { + final FileInputStream fileInputStream = new FileInputStream(file); + fileInputStream.read(dataEntry); + fileInputStream.close(); + } catch (FileNotFoundException e) { + log.severe("File Not Found."); + e.printStackTrace(); + } catch (IOException e1) { + log.severe("Error Reading The File."); + e1.printStackTrace(); + } + return dataEntry; + } else { + log.severe("file not found: " + file.getAbsolutePath()); + return null; + } + + } catch (SAXException sxe) { + // Error generated during parsing) + Exception x = sxe; + if (sxe.getException() != null) + x = sxe.getException(); + x.printStackTrace(); + log.severe(x.getMessage()); + + } catch (ParserConfigurationException pce) { + // Parser with specified options can't be built + pce.printStackTrace(); + log.severe(pce.getMessage()); + + } catch (IOException ioe) { + // I/O error + ioe.printStackTrace(); + log.severe(ioe.getMessage()); + } + + return null; + } + + @Override + public void save(MImage image, MStorageProvider prov,byte[] inflatedData) { + if (inflatedData == null || inflatedData.length == 0) { + image.setByteData(null); + delete(image, prov); + return; + } + + if(image.get_ID()==0){ + //set binary data otherwise save will fail + image.setByteData(new byte[]{'0'}); + buffer = inflatedData; + } else { + write(image, prov, inflatedData); + } + + } + + private void write(MImage image, MStorageProvider prov, byte[] inflatedData) { + BufferedOutputStream out = null; + try { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + String imagePathRoot = getImagePathRoot(prov); + if ("".equals(imagePathRoot)) { + throw new IllegalArgumentException("no storage path defined"); + } + // create destination folder + StringBuilder msgfile = new StringBuilder().append(imagePathRoot) + .append(image.getImageStoragePath()); + final File destFolder = new File(msgfile.toString()); + if (!destFolder.exists()) { + if (!destFolder.mkdirs()) { + log.warning("unable to create folder: " + destFolder.getPath()); + } + } + + // write to path + msgfile = new StringBuilder().append(imagePathRoot).append(File.separator) + .append(image.getImageStoragePath()).append(image.get_ID()); + final File destFile = new File(msgfile.toString()); + + out = new BufferedOutputStream(new FileOutputStream(destFile)); + out.write(inflatedData); + out.flush(); + + //create xml entry + final DocumentBuilder builder = factory.newDocumentBuilder(); + final Document document = builder.newDocument(); + final Element root = document.createElement("image"); + document.appendChild(root); + document.setXmlStandalone(true); + final Element entry = document.createElement("entry"); + StringBuilder msgsat = new StringBuilder(IMAGE_FOLDER_PLACEHOLDER).append(image.getImageStoragePath()).append(image.get_ID()); + entry.setAttribute("file", msgsat.toString()); + root.appendChild(entry); + final Source source = new DOMSource(document); + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + final Result result = new StreamResult(bos); + final Transformer xformer = TransformerFactory.newInstance().newTransformer(); + xformer.transform(source, result); + final byte[] xmlData = bos.toByteArray(); + if (log.isLoggable(Level.FINE)) log.fine(bos.toString()); + //store xml in db + image.setByteData(xmlData); + + } catch (Exception e) { + log.log(Level.SEVERE, "saveLOBData", e); + image.setByteData(null); + throw new RuntimeException(e); + } finally { + if(out != null){ + try { + out.close(); + } catch (Exception e) { } + } + } + } + + private String getImagePathRoot(MStorageProvider prov) { + String imagePathRoot = prov.getFolder(); + if (imagePathRoot == null) + imagePathRoot = ""; + if (Util.isEmpty(imagePathRoot)) { + log.severe("no image Path defined"); + } else if (!imagePathRoot.endsWith(File.separator)){ + imagePathRoot = imagePathRoot + File.separator; + log.fine(imagePathRoot); + } + return imagePathRoot; + } + + @Override + public boolean delete(MImage image, MStorageProvider prov) { + String imagePathRoot = getImagePathRoot(prov); + if ("".equals(imagePathRoot)) { + throw new IllegalArgumentException("no image path defined"); + } + StringBuilder msgfile = new StringBuilder().append(imagePathRoot) + .append(image.getImageStoragePath()).append(image.getAD_Image_ID()); + + File file=new File(msgfile.toString()); + if (file !=null && file.exists()) { + if (!file.delete()) { + log.warning("unable to delete " + file.getAbsolutePath()); + return false; + } + } + return true; + } + + @Override + public boolean isPendingFlush() { + return buffer != null && buffer.length > 0; + } + + @Override + public void flush(MImage image, MStorageProvider prov) { + if (buffer != null && buffer.length > 0) { + write(image, prov, buffer); + buffer = null; + } + } + +} diff --git a/org.adempiere.base/src/org/compiere/model/MArchive.java b/org.adempiere.base/src/org/compiere/model/MArchive.java index 3d9207517c..4948d11089 100644 --- a/org.adempiere.base/src/org/compiere/model/MArchive.java +++ b/org.adempiere.base/src/org/compiere/model/MArchive.java @@ -39,7 +39,7 @@ public class MArchive extends X_AD_Archive { /** * */ - private static final long serialVersionUID = 3217541537768473865L; + private static final long serialVersionUID = -9116541441191978777L; /** * Get Archives @@ -269,7 +269,8 @@ public class MArchive extends X_AD_Archive { return true; } // beforeSave - protected boolean beforeDelete () + @Override + protected boolean postDelete() { IArchiveStore prov = provider.getArchiveStore(); if (prov != null) @@ -278,4 +279,11 @@ public class MArchive extends X_AD_Archive { } + @Override + protected void saveNew_afterSetID() + { + IArchiveStore prov = provider.getArchiveStore(); + if (prov != null && prov.isPendingFlush()) + prov.flush(this,provider); + } } // MArchive diff --git a/org.adempiere.base/src/org/compiere/model/MAttachment.java b/org.adempiere.base/src/org/compiere/model/MAttachment.java index 1bcb950e79..e1cb5aaee5 100644 --- a/org.adempiere.base/src/org/compiere/model/MAttachment.java +++ b/org.adempiere.base/src/org/compiere/model/MAttachment.java @@ -57,7 +57,7 @@ public class MAttachment extends X_AD_Attachment /** * */ - private static final long serialVersionUID = -8261865873158774665L; + private static final long serialVersionUID = 6596285414376249694L; /** * @@ -502,28 +502,20 @@ public class MAttachment extends X_AD_Attachment return saveLOBData(); // save in BinaryData } // beforeSave - /** - * Executed before Delete operation. - * @return true if record can be deleted - */ - protected boolean beforeDelete () - { - return deleteLOBData(); - } - /** * Delete Entry Data in Zip File format * @return true if saved */ - private boolean deleteLOBData() + @Override + protected boolean postDelete() { if (m_items == null) loadLOBData(); IAttachmentStore prov = provider.getAttachmentStore(); if (prov != null) return prov.delete(this,provider); - return false; - } // beforeDelete + return true; + } // postDelete /************************************************************************** * Test diff --git a/org.adempiere.base/src/org/compiere/model/MBPartner.java b/org.adempiere.base/src/org/compiere/model/MBPartner.java index 6e0e0293ba..f48e61ece5 100644 --- a/org.adempiere.base/src/org/compiere/model/MBPartner.java +++ b/org.adempiere.base/src/org/compiere/model/MBPartner.java @@ -47,7 +47,7 @@ public class MBPartner extends X_C_BPartner /** * */ - private static final long serialVersionUID = -255154524310324997L; + private static final long serialVersionUID = 5534148976588041343L; /** * Get Empty Template Business Partner @@ -1006,4 +1006,16 @@ public class MBPartner extends X_C_BPartner return success; } // afterDelete + @Override + protected boolean postDelete() { + if (getLogo_ID() > 0) { + MImage img = new MImage(getCtx(), getLogo_ID(), get_TrxName()); + if (!img.delete(true)) { + log.warning("Associated image could not be deleted for bpartner - AD_Image_ID=" + getLogo_ID()); + return false; + } + } + return true; + } + } // MBPartner diff --git a/org.adempiere.base/src/org/compiere/model/MImage.java b/org.adempiere.base/src/org/compiere/model/MImage.java index b0f965684a..026bed6d0c 100644 --- a/org.adempiere.base/src/org/compiere/model/MImage.java +++ b/org.adempiere.base/src/org/compiere/model/MImage.java @@ -21,6 +21,7 @@ import java.awt.Image; import java.awt.MediaTracker; import java.awt.Toolkit; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; @@ -49,6 +50,8 @@ public class MImage extends X_AD_Image */ private static final long serialVersionUID = -7361463683427300715L; + private MStorageProvider provider; + /** * Get MImage from Cache * @param ctx context @@ -84,6 +87,7 @@ public class MImage extends X_AD_Image super (ctx, AD_Image_ID, trxName); if (AD_Image_ID < 1) setName("-"); + initImageStoreDetails(ctx, trxName); } // MImage /** @@ -95,6 +99,7 @@ public class MImage extends X_AD_Image public MImage (Properties ctx, ResultSet rs, String trxName) { super(ctx, rs, trxName); + initImageStoreDetails(ctx, trxName); } // MImage @@ -233,20 +238,31 @@ public class MImage extends X_AD_Image * Set Binary Data * @param BinaryData data */ + @Override public void setBinaryData (byte[] BinaryData) { m_image = null; m_icon = null; - super.setBinaryData (BinaryData); + IImageStore prov = provider.getImageStore(); + if (prov != null) + prov.save(this,provider,BinaryData); } // setBinaryData + @Override + public byte[] getBinaryData() { + IImageStore prov = provider.getImageStore(); + if (prov != null) + return prov.load(this,provider); + return null; + } + /** * Get Data * @return data */ public byte[] getData() { - byte[] data = super.getBinaryData (); + byte[] data = getBinaryData (); if (data != null) return data; // From URL @@ -311,5 +327,47 @@ public class MImage extends X_AD_Image setAD_Org_ID(0); return true; } // beforeSave + + public String getImageStoragePath() { + StringBuilder path = new StringBuilder("AD_Image").append(File.separator) + .append(this.getAD_Client_ID()).append(File.separator); + return path.toString(); + } + /** + * @param ctx + * @param trxName + */ + private void initImageStoreDetails(Properties ctx, String trxName) { + MClientInfo clientInfo = MClientInfo.get(ctx); + provider=new MStorageProvider(ctx, clientInfo.getStorageImage_ID(), trxName); + } + + public void setStorageProvider(MStorageProvider p) { + provider = p; + } + + public byte[] getByteData(){ + return super.getBinaryData(); + } + + public void setByteData(byte[] BinaryData){ + super.setBinaryData(BinaryData); + } + + @Override + protected boolean postDelete() { + IImageStore prov = provider.getImageStore(); + if (prov != null) + return prov.delete(this,provider); + return true; + } + + @Override + protected void saveNew_afterSetID() + { + IImageStore prov = provider.getImageStore(); + if (prov != null && prov.isPendingFlush()) + prov.flush(this, provider); + } } // MImage diff --git a/org.adempiere.base/src/org/compiere/model/MPOSKey.java b/org.adempiere.base/src/org/compiere/model/MPOSKey.java index 207ccb3396..361a997c9d 100644 --- a/org.adempiere.base/src/org/compiere/model/MPOSKey.java +++ b/org.adempiere.base/src/org/compiere/model/MPOSKey.java @@ -28,11 +28,10 @@ import java.util.Properties; */ public class MPOSKey extends X_C_POSKey { - /** * */ - private static final long serialVersionUID = -5810613982853803837L; + private static final long serialVersionUID = 2595668386249398840L; /** * Standard Constructor @@ -56,4 +55,16 @@ public class MPOSKey extends X_C_POSKey super(ctx, rs, trxName); } // MPOSKey + @Override + protected boolean postDelete() { + if (getAD_Image_ID() > 0) { + MImage img = new MImage(getCtx(), getAD_Image_ID(), get_TrxName()); + if (!img.delete(true)) { + log.warning("Associated image could not be deleted for POS Key - AD_Image_ID=" + getAD_Image_ID()); + return false; + } + } + return true; + } + } // MPOSKey diff --git a/org.adempiere.base/src/org/compiere/model/MStorageProvider.java b/org.adempiere.base/src/org/compiere/model/MStorageProvider.java index c3fa25aad5..af50b9b0f3 100644 --- a/org.adempiere.base/src/org/compiere/model/MStorageProvider.java +++ b/org.adempiere.base/src/org/compiere/model/MStorageProvider.java @@ -63,4 +63,16 @@ public class MStorageProvider extends X_AD_StorageProvider { return store; } + public IImageStore getImageStore() { + ServiceQuery query=new ServiceQuery(); + String method = this.getMethod(); + if (method == null) + method = "DB"; + query.put("method", method); + IImageStore store = Service.locator().locate(IImageStore.class, query).getService(); + if (store == null) { + throw new AdempiereException("No image storage provider found"); + } + return store; + } } diff --git a/org.adempiere.base/src/org/compiere/model/MSysConfig.java b/org.adempiere.base/src/org/compiere/model/MSysConfig.java index 209626cc5d..8702948d4b 100644 --- a/org.adempiere.base/src/org/compiere/model/MSysConfig.java +++ b/org.adempiere.base/src/org/compiere/model/MSysConfig.java @@ -42,7 +42,7 @@ public class MSysConfig extends X_AD_SysConfig /** * */ - private static final long serialVersionUID = -4014355086112902334L; + private static final long serialVersionUID = -9208749663408576569L; public static final String ADDRESS_VALIDATION = "ADDRESS_VALIDATION"; public static final String ALERT_SEND_ATTACHMENT_AS_XLS = "ALERT_SEND_ATTACHMENT_AS_XLS"; @@ -99,6 +99,7 @@ public class MSysConfig extends X_AD_SysConfig public static final String HTML_REPORT_THEME = "HTML_REPORT_THEME"; public static final String IBAN_VALIDATION = "IBAN_VALIDATION"; public static final String IDENTIFIER_SEPARATOR = "IDENTIFIER_SEPARATOR"; + public static final String IMAGE_DB_STORAGE_SAVE_AS_ZIP = "IMAGE_DB_STORAGE_SAVE_AS_ZIP"; public static final String INFO_DEFAULTSELECTED = "INFO_DEFAULTSELECTED"; public static final String INFO_DOUBLECLICKTOGGLESSELECTION = "INFO_DOUBLECLICKTOGGLESSELECTION"; public static final String Invoice_ReverseUseNewNumber = "Invoice_ReverseUseNewNumber"; diff --git a/org.adempiere.base/src/org/compiere/model/MUser.java b/org.adempiere.base/src/org/compiere/model/MUser.java index 5c6410730b..163cfaf6f5 100644 --- a/org.adempiere.base/src/org/compiere/model/MUser.java +++ b/org.adempiere.base/src/org/compiere/model/MUser.java @@ -33,7 +33,6 @@ import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import org.adempiere.exceptions.DBException; -import org.codehaus.groovy.classgen.GeneratorContext; import org.compiere.util.CCache; import org.compiere.util.CLogger; import org.compiere.util.DB; @@ -59,7 +58,7 @@ public class MUser extends X_AD_User /** * */ - private static final long serialVersionUID = 7996468236476384128L; + private static final long serialVersionUID = 1366564982801896588L; /** * Get active Users of BPartner @@ -1105,4 +1104,17 @@ public class MUser extends X_AD_User } return super.afterSave(newRecord, success); } + + @Override + protected boolean postDelete() { + if (getAD_Image_ID() > 0) { + MImage img = new MImage(getCtx(), getAD_Image_ID(), get_TrxName()); + if (!img.delete(true)) { + log.warning("Associated image could not be deleted for user - AD_Image_ID=" + getAD_Image_ID()); + return false; + } + } + return true; + } + } // MUser diff --git a/org.adempiere.base/src/org/compiere/model/PO.java b/org.adempiere.base/src/org/compiere/model/PO.java index 885c3636fd..ef7f0b204c 100644 --- a/org.adempiere.base/src/org/compiere/model/PO.java +++ b/org.adempiere.base/src/org/compiere/model/PO.java @@ -110,7 +110,7 @@ public abstract class PO /** * */ - private static final long serialVersionUID = -1743619574547406959L; + private static final long serialVersionUID = -1330388218446118451L; public static final String LOCAL_TRX_PREFIX = "POSave"; @@ -2782,6 +2782,7 @@ public abstract class PO } m_IDs[0] = Integer.valueOf(no); set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]); + saveNew_afterSetID(); } //uuid secondary key int uuidIndex = p_info.getColumnIndex(getUUIDColumnName()); @@ -3096,6 +3097,13 @@ public abstract class PO return 0; } // saveNew_getID + /** + * Call after ID have been assigned for new record + */ + protected void saveNew_afterSetID() + { + + } /** * Create Single/Multi Key Where Clause @@ -3496,6 +3504,10 @@ public abstract class PO // Reset if (success) { + if (!postDelete()) { + log.warning("postDelete failed"); + } + //osgi event handler Event event = EventManager.newEvent(IEventTopics.PO_POST_DELETE, this); EventManager.getInstance().postEvent(event); @@ -3595,6 +3607,14 @@ public abstract class PO return success; } // afterDelete + /** + * Executed after the Delete operation is committed in the database. + * @return true if post delete is a success + */ + protected boolean postDelete() + { + return true; + } /** * Insert (missing) Translation Records diff --git a/org.adempiere.base/src/org/compiere/model/PO_Record.java b/org.adempiere.base/src/org/compiere/model/PO_Record.java index c1e58926f9..1c8ca9db95 100644 --- a/org.adempiere.base/src/org/compiere/model/PO_Record.java +++ b/org.adempiere.base/src/org/compiere/model/PO_Record.java @@ -109,15 +109,27 @@ public class PO_Record if (s_cascades[i] != AD_Table_ID) { Object[] params = new Object[]{Integer.valueOf(AD_Table_ID), Integer.valueOf(Record_ID)}; - StringBuffer sql = new StringBuffer ("DELETE FROM ") - .append(s_cascadeNames[i]) - .append(" WHERE AD_Table_ID=? AND Record_ID=?"); - int no = DB.executeUpdate(sql.toString(), params, false, trxName); - if (no > 0) { - if (log.isLoggable(Level.CONFIG)) log.config(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no); - } else if (no < 0) { - log.severe(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no); - return false; + if (s_cascadeNames[i].equals(X_AD_Attachment.Table_Name) || s_cascadeNames[i].equals(X_AD_Archive.Table_Name)) + { + Query query = new Query(Env.getCtx(), s_cascadeNames[i], "AD_Table_ID=? AND Record_ID=?", trxName); + List list = query.setParameters(params).list(); + for(PO po : list) + { + po.deleteEx(true); + } + } + else + { + StringBuffer sql = new StringBuffer ("DELETE FROM ") + .append(s_cascadeNames[i]) + .append(" WHERE AD_Table_ID=? AND Record_ID=?"); + int no = DB.executeUpdate(sql.toString(), params, false, trxName); + if (no > 0) { + if (log.isLoggable(Level.CONFIG)) log.config(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no); + } else if (no < 0) { + log.severe(s_cascadeNames[i] + " (" + AD_Table_ID + "/" + Record_ID + ") #" + no); + return false; + } } } } diff --git a/org.adempiere.base/src/org/compiere/model/X_AD_ClientInfo.java b/org.adempiere.base/src/org/compiere/model/X_AD_ClientInfo.java index 2d08349c1d..3f67179894 100644 --- a/org.adempiere.base/src/org/compiere/model/X_AD_ClientInfo.java +++ b/org.adempiere.base/src/org/compiere/model/X_AD_ClientInfo.java @@ -30,7 +30,7 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent /** * */ - private static final long serialVersionUID = 20191121L; + private static final long serialVersionUID = 20200226L; /** Standard Constructor */ public X_AD_ClientInfo (Properties ctx, int AD_ClientInfo_ID, String trxName) @@ -767,4 +767,32 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent return 0; return ii.intValue(); } + + public org.compiere.model.I_AD_StorageProvider getStorageImage() throws RuntimeException + { + return (org.compiere.model.I_AD_StorageProvider)MTable.get(getCtx(), org.compiere.model.I_AD_StorageProvider.Table_Name) + .getPO(getStorageImage_ID(), get_TrxName()); } + + /** Set Image Store. + @param StorageImage_ID + Storage provider for Image + */ + public void setStorageImage_ID (int StorageImage_ID) + { + if (StorageImage_ID < 1) + set_Value (COLUMNNAME_StorageImage_ID, null); + else + set_Value (COLUMNNAME_StorageImage_ID, Integer.valueOf(StorageImage_ID)); + } + + /** Get Image Store. + @return Storage provider for Image + */ + public int getStorageImage_ID () + { + Integer ii = (Integer)get_Value(COLUMNNAME_StorageImage_ID); + if (ii == null) + return 0; + return ii.intValue(); + } } \ No newline at end of file diff --git a/org.adempiere.ui.zk/META-INF/MANIFEST.MF b/org.adempiere.ui.zk/META-INF/MANIFEST.MF index 7287571745..49075c207a 100644 --- a/org.adempiere.ui.zk/META-INF/MANIFEST.MF +++ b/org.adempiere.ui.zk/META-INF/MANIFEST.MF @@ -49,6 +49,7 @@ Import-Package: groovy.transform.stc;version="2.4.7", org.jfree.data.time, org.jfree.util, org.osgi.framework;version="1.7.0", + org.osgi.framework.wiring;version="1.2.0", org.osgi.service.component.annotations;version="1.3.0", org.osgi.service.event;version="1.3.0", org.osgi.util.tracker;version="1.5.0", diff --git a/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/AtmosphereServerPush.java b/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/AtmosphereServerPush.java index ff7d397c87..4891a7fb85 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/AtmosphereServerPush.java +++ b/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/AtmosphereServerPush.java @@ -122,7 +122,7 @@ public class AtmosphereServerPush implements ServerPush { private boolean commitResponse() throws IOException { AtmosphereResource resource = this.resource.getAndSet(null); - if (resource != null && resource.isSuspended()) { + if (resource != null) { resource.resume(); return true; } @@ -268,7 +268,7 @@ public class AtmosphereServerPush implements ServerPush { } if (!resource.isSuspended()) { - resource.suspend(-1); + resource.suspend(); } this.resource.set(resource); diff --git a/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/ZkAtmosphereHandler.java b/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/ZkAtmosphereHandler.java index 0d5aedbdb6..4dd9f6a713 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/ZkAtmosphereHandler.java +++ b/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/ZkAtmosphereHandler.java @@ -29,6 +29,8 @@ import org.atmosphere.cpr.AtmosphereRequest; import org.atmosphere.cpr.AtmosphereResource; import org.atmosphere.cpr.AtmosphereResourceEvent; import org.atmosphere.cpr.AtmosphereResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Session; import org.zkoss.zk.ui.http.WebManager; @@ -41,6 +43,8 @@ import org.zkoss.zk.ui.sys.WebAppCtrl; */ public class ZkAtmosphereHandler implements AtmosphereHandler { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + @Override public void destroy() { } @@ -49,6 +53,8 @@ public class ZkAtmosphereHandler implements AtmosphereHandler { if (session.getWebApp() instanceof WebAppCtrl) { WebAppCtrl webAppCtrl = (WebAppCtrl) session.getWebApp(); Desktop desktop = webAppCtrl.getDesktopCache(session).getDesktopIfAny(dtid); + if (desktop == null) + log.warn("Could not find desktop: " + dtid); return new Either("Could not find desktop", desktop); } return new Either("Webapp does not implement WebAppCtrl", null); @@ -101,10 +107,12 @@ public class ZkAtmosphereHandler implements AtmosphereHandler { private Either getSession(AtmosphereResource resource, HttpServletRequest request) { Session session = WebManager.getSession(resource.getAtmosphereConfig().getServletContext(), request, false); - if (session == null) + if (session == null) { + log.warn("Could not find session: " + request.getRequestURI()); return new Either("Could not find session", null); - else + } else { return new Either(null, session); + } } @Override @@ -116,6 +124,7 @@ public class ZkAtmosphereHandler implements AtmosphereHandler { Either serverPushEither = getServerPush(resource); String error = serverPushEither.getLeftValue(); if (error != null && serverPushEither.getRightValue() == null) { + log.warn("Bad Request. Error="+error+", Request="+resource.getRequest().getRequestURI()); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.getWriter().write(error); response.getWriter().flush(); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WImageEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WImageEditor.java index 8fd0185df7..534e194440 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WImageEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WImageEditor.java @@ -178,6 +178,10 @@ public class WImageEditor extends WEditor // ValueChangeEvent vce = new ValueChangeEvent(WImageEditor.this, gridField.getColumnName(), oldValue, newValue); fireValueChange(vce); + if (oldValue == null && newValue != null && getGridField() != null && getGridField().getGridTab() != null) { + // save automatically when creating a new image + getGridField().getGridTab().dataSave(false); + } } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/AboutWindow.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/AboutWindow.java index 93ad881d7b..86e876bf91 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/AboutWindow.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/AboutWindow.java @@ -65,6 +65,7 @@ import org.compiere.util.Util; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; +import org.osgi.framework.wiring.BundleRevision; import org.zkoss.util.media.AMedia; import org.zkoss.zhtml.Pre; import org.zkoss.zhtml.Text; @@ -445,16 +446,37 @@ public class AboutWindow extends Window implements EventListener { if (bundle == null) return; int state = bundle.getState(); + boolean isFragment = false; + BundleRevision rev = bundle.adapt(BundleRevision.class); + if (rev != null) { + isFragment = (rev.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0; + } + /* + boolean hasFragments = false; + if (!isFragment) { + if (rev.getWiring() != null) { + if (rev.getWiring().getProvidedWires(BundleRevision.HOST_NAMESPACE).size() > 0) { + hasFragments = true; + } + } + } + */ if (bundle.getBundleId() == 0) { // bundle 0 cannot be stopped } else if (state == Bundle.ACTIVE) { pluginActions.getItemAtIndex(PLUGIN_ACTION_STOP).setVisible(true); } else if (state == Bundle.RESOLVED) { - pluginActions.getItemAtIndex(PLUGIN_ACTION_START).setVisible(true); + if (!isFragment) { + pluginActions.getItemAtIndex(PLUGIN_ACTION_START).setVisible(true); + } } else if (state == Bundle.INSTALLED) { - // no options yet for installed + if (!isFragment) { + pluginActions.getItemAtIndex(PLUGIN_ACTION_START).setVisible(true); + } } else if (state == Bundle.STARTING) { - // no options yet for starting + if (!isFragment) { + pluginActions.getItemAtIndex(PLUGIN_ACTION_START).setVisible(true); + } } else if (state == Bundle.STOPPING) { // no options yet for stopping } else if (state == Bundle.UNINSTALLED) { @@ -466,8 +488,7 @@ public class AboutWindow extends Window implements EventListener { Bundle retValue = null; int idx = pluginsTable.getSelectedIndex(); if (idx >= 0) { - Integer selectedPlugin = (Integer) pluginsTable.getModel().getDataAt(idx, 1); - Vector pluginVector = pluginData.get(selectedPlugin); + Vector pluginVector = pluginData.get(idx); int pluginId = ((IDColumn)pluginVector.get(0)).getRecord_ID(); BundleContext bundleCtx = WebUIActivator.getBundleContext(); retValue = bundleCtx.getBundle(pluginId); @@ -505,6 +526,7 @@ public class AboutWindow extends Window implements EventListener { } private void refreshPluginTable() { + int idx = pluginsTable.getSelectedIndex(); pluginsTable.getModel().removeAll(pluginData); pluginData.removeAllElements(); @@ -521,6 +543,7 @@ public class AboutWindow extends Window implements EventListener { } ListModelTable model = new ListModelTable(pluginData); pluginsTable.setData(model, pluginColumnNames); + pluginsTable.setSelectedIndex(idx); } protected Tabpanel createInfo() { diff --git a/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/serverpush.js b/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/serverpush.js index 45f9fd78aa..eabe053fe0 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/serverpush.js +++ b/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/serverpush.js @@ -23,7 +23,7 @@ trace: false, ajaxOptions: { url: zk.ajaxURI("/comet", {au: true}), - type: "GET", + type: "POST", cache: false, async: true, global: false, @@ -35,6 +35,7 @@ this.timeout = timeout; this.ajaxOptions.data = { dtid: this.desktop.id }; this.ajaxOptions.timeout = this.timeout; + this.ajaxOptions.url = zk.ajaxURI("/comet", {au: true,desktop:this.desktop.id,ignoreSession:false}), this.trace = trace; var me = this; this.ajaxOptions.error = function(jqxhr, textStatus, errorThrown) {