IDEMPIERE-669 Zk: User Feedback Service.

This commit is contained in:
Heng Sin Low 2013-02-27 23:21:27 +08:00
parent 184f457783
commit cb21a92f13
19 changed files with 885 additions and 105 deletions

View File

@ -51,17 +51,30 @@ public class MAttachment extends X_AD_Attachment
*/
private static final long serialVersionUID = -8261865873158774665L;
/**
*
* @param ctx
* @param AD_Table_ID
* @param Record_ID
* @return attachment or null
*/
public static MAttachment get (Properties ctx, int AD_Table_ID, int Record_ID)
{
return get(ctx, AD_Table_ID, Record_ID, (String)null);
}
/**
* Get Attachment (if there are more than one attachment it gets the first in no specific order)
* @param ctx context
* @param AD_Table_ID table
* @param Record_ID record
* @param trxName
* @return attachment or null
*/
public static MAttachment get (Properties ctx, int AD_Table_ID, int Record_ID)
public static MAttachment get (Properties ctx, int AD_Table_ID, int Record_ID, String trxName)
{
final String whereClause = I_AD_Attachment.COLUMNNAME_AD_Table_ID+"=? AND "+I_AD_Attachment.COLUMNNAME_Record_ID+"=?";
MAttachment retValue = new Query(ctx,I_AD_Attachment.Table_Name,whereClause, null)
MAttachment retValue = new Query(ctx,I_AD_Attachment.Table_Name,whereClause, trxName)
.setParameters(AD_Table_ID, Record_ID)
.first();
return retValue;

View File

@ -4,7 +4,8 @@ Bundle-Name: Zk Web Client
Bundle-SymbolicName: org.adempiere.ui.zk;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: javax.servlet,
Import-Package: javax.activation;version="1.1.1",
javax.servlet,
javax.servlet.http,
metainfo.zk,
org.apache.commons.codec.binary,
@ -58,4 +59,4 @@ Bundle-Activator: org.adempiere.webui.WebUIActivator
Eclipse-ExtensibleAPI: true
Eclipse-RegisterBuddy: org.zkoss.zk.library
Web-ContextPath: webui
Service-Component: OSGI-INF/reportviewerprovider.xml, OSGI-INF/defaultinfofactory.xml, OSGI-INF/defaulteditorfactory.xml, OSGI-INF/jrviewerprovider.xml, OSGI-INF/resourcefinder.xml, OSGI-INF/defaultpaymentformfactory.xml, OSGI-INF/processfactory.xml, OSGI-INF/defaultprintshippinglabel.xml, OSGI-INF/defaultcreatefromfactory.xml, OSGI-INF/defaultformfactory.xml
Service-Component: OSGI-INF/reportviewerprovider.xml, OSGI-INF/defaultinfofactory.xml, OSGI-INF/defaulteditorfactory.xml, OSGI-INF/jrviewerprovider.xml, OSGI-INF/resourcefinder.xml, OSGI-INF/defaultpaymentformfactory.xml, OSGI-INF/processfactory.xml, OSGI-INF/defaultprintshippinglabel.xml, OSGI-INF/defaultcreatefromfactory.xml, OSGI-INF/defaultformfactory.xml, OSGI-INF/feedbackservice.xml

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.adempiere.ui.zk">
<implementation class="org.adempiere.webui.factory.DefaultFeedbackService"/>
<service>
<provide interface="org.adempiere.webui.factory.IFeedbackService"/>
</service>
</scr:component>

View File

@ -436,10 +436,14 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
ClientInfoEvent c = (ClientInfoEvent)event;
clientInfo = new ClientInfo();
clientInfo.colorDepth = c.getColorDepth();
clientInfo.screenHeight = c.getScreenHeight();
clientInfo.screenWidth = c.getScreenWidth();
clientInfo.devicePixelRatio = c.getDevicePixelRatio();
clientInfo.desktopHeight = c.getDesktopHeight();
clientInfo.desktopWidth = c.getDesktopWidth();
clientInfo.desktopXOffset = c.getDesktopXOffset();
clientInfo.desktopYOffset = c.getDesktopYOffset();
clientInfo.orientation = c.getOrientation();
clientInfo.timeZone = c.getTimeZone();
if (appDesktop != null)
appDesktop.setClientInfo(clientInfo);

View File

@ -37,7 +37,42 @@ public class ClientInfo implements Serializable {
public int desktopYOffset;
public int screenHeight;
public int screenWidth;
public String orientation;
public TimeZone timeZone;
public String userAgent;
public boolean tablet;
public double devicePixelRatio;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("User Agent: ")
.append(userAgent)
.append("\r\n")
.append("Time Zone: ")
.append(timeZone.getID())
.append("\r\n")
.append("Screen Size: ")
.append(screenWidth)
.append(" x ")
.append(screenHeight)
.append("\r\n")
.append("Browser Desktop Size: ")
.append(desktopWidth)
.append(" x ")
.append(desktopHeight)
.append("\r\n")
.append("Orientation: ")
.append(orientation)
.append("\r\n")
.append("Color Depth: ")
.append(colorDepth)
.append("\r\n")
.append("Pixel Ratio: ")
.append(devicePixelRatio);
return builder.toString();
}
}

View File

@ -0,0 +1,334 @@
/******************************************************************************
* Copyright (C) 2013 Heng Sin Low *
* Copyright (C) 2013 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.adempiere.webui.apps;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.activation.DataSource;
import org.adempiere.webui.component.AttachmentItem;
import org.adempiere.webui.component.Button;
import org.adempiere.webui.component.ConfirmPanel;
import org.adempiere.webui.component.Grid;
import org.adempiere.webui.component.GridFactory;
import org.adempiere.webui.component.Label;
import org.adempiere.webui.component.Row;
import org.adempiere.webui.component.Rows;
import org.adempiere.webui.component.Textbox;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.editor.WTableDirEditor;
import org.adempiere.webui.util.FeedbackManager;
import org.adempiere.webui.window.FDialog;
import org.apache.commons.io.IOUtils;
import org.compiere.model.MAttachment;
import org.compiere.model.MColumn;
import org.compiere.model.MLookup;
import org.compiere.model.MLookupFactory;
import org.compiere.model.MRequest;
import org.compiere.model.MRole;
import org.compiere.util.ByteArrayDataSource;
import org.compiere.util.CLogger;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Trx;
import org.zkoss.util.media.Media;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.UploadEvent;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Div;
import org.zkoss.zul.Separator;
import org.zkoss.zul.Vlayout;
/**
*
* @author hengsin
*
*/
public class FeedbackRequestWindow extends Window implements EventListener<Event> {
/**
* generated serial id
*/
private static final long serialVersionUID = 8586980192148533197L;
private static CLogger log = CLogger.getCLogger(FeedbackRequestWindow.class);
private WTableDirEditor requestTypeField, priorityField, salesRepField;
private Textbox txtSummary;
private ConfirmPanel confirmPanel;
private List<DataSource> attachments = new ArrayList<DataSource>();
private Div attachmentBox;
public FeedbackRequestWindow() {
super();
setTitle(Msg.getMsg(Env.getCtx(), "RequestNew"));
setAttribute(Window.MODE_KEY, Window.MODE_HIGHLIGHTED);
setWidth("400px");
this.setBorder("normal");
this.setClosable(true);
boolean readOnly = !MRole.getDefault().canUpdate(
Env.getAD_Client_ID(Env.getCtx()), Env.getAD_Org_ID(Env.getCtx()),
MRequest.Table_ID, 0, false);
if (readOnly)
{
throw new RuntimeException(Msg.getMsg(Env.getCtx(), "AccessTableNoUpdate"));
}
Label lblRequestType = new Label("Request Type");
Label lblPriority = new Label("Priority");
Label lblSummary = new Label("Summary");
Label lblSalesRep = new Label("Sales Representative");
int columnID = MColumn.getColumn_ID(MRequest.Table_Name, MRequest.COLUMNNAME_R_RequestType_ID);
MLookup lookup = MLookupFactory.get(Env.getCtx(), 0, 0, columnID, DisplayType.TableDir);
requestTypeField = new WTableDirEditor("R_RequestType_ID", true, false, true, lookup);
requestTypeField.setValue(Env.getContext(Env.getCtx(), "P232|R_RequestType_ID"));
if(requestTypeField.getValue() == null || requestTypeField.getValue().equals(""))
if(requestTypeField.getComponent().getItemCount() > 1)
requestTypeField.setValue(requestTypeField.getComponent().getItemAtIndex(1).getValue());
columnID = MColumn.getColumn_ID(MRequest.Table_Name, MRequest.COLUMNNAME_Priority);
lookup = MLookupFactory.get(Env.getCtx(), 0, 0, columnID, DisplayType.List);
priorityField = new WTableDirEditor("Priority", true, false, true, lookup);
priorityField.setValue(Env.getContext(Env.getCtx(), "P232|Priority"));
if(priorityField.getValue() == null || priorityField.getValue().equals(""))
if(priorityField.getComponent().getItemCount() > 1)
priorityField.setValue(priorityField.getComponent().getItemAtIndex(1).getValue());
columnID = MColumn.getColumn_ID(MRequest.Table_Name, MRequest.COLUMNNAME_SalesRep_ID);
lookup = MLookupFactory.get(Env.getCtx(), 0, 0, columnID, DisplayType.TableDir);
salesRepField = new WTableDirEditor("SalesRep_ID", true, false, true, lookup);
salesRepField.setValue(Env.getContextAsInt(Env.getCtx(), "SalesRep_ID"));
if(salesRepField.getValue() == null || salesRepField.getValue().equals("0"))
if(salesRepField.getComponent().getItemCount() > 1)
salesRepField.setValue(salesRepField.getComponent().getItemAtIndex(1).getValue());
txtSummary = new Textbox();
txtSummary.setRows(10);
txtSummary.setWidth("95%");
confirmPanel = new ConfirmPanel(true);
confirmPanel.addActionListener(this);
Grid grid = GridFactory.newGridLayout();
grid.setVflex("min");
Rows rows = new Rows();
grid.appendChild(rows);
Row row = rows.newRow();
row.setStyle("padding: 4px 4px 0px 6px");
row.appendChild(lblRequestType);
row = rows.newRow();
row.setStyle("padding: 0px 4px 4px 6px");
row.appendChild(requestTypeField.getComponent());
row = rows.newRow();
row.setStyle("padding: 4px 4px 0px 6px");
row.appendChild(lblPriority);
row = rows.newRow();
row.setStyle("padding: 0px 4px 4px 6px");
row.appendChild(priorityField.getComponent());
row = rows.newRow();
row.setStyle("padding: 4px 4px 0px 6px");
row.appendChild(lblSummary);
row = rows.newRow();
row.setStyle("padding: 0px 4px 4px 6px");
row.appendChild(txtSummary);
row = rows.newRow();
row.setStyle("padding: 4px 4px 0px 6px");
row.appendChild(lblSalesRep);
row = rows.newRow();
row.setStyle("padding: 0px 4px 4px 6px");
row.appendChild(salesRepField.getComponent());
row = rows.newRow();
row.setStyle("padding: 4px 4px 0px 6px");
row.appendChild(new Label(Msg.getMsg(Env.getCtx(), "Attachment")));
attachmentBox = new Div();
attachmentBox.setHflex("1");
attachmentBox.setVflex("1");
row = rows.newRow();
row.setStyle("padding: 0px 4px 4px 6px");
row.appendChild(attachmentBox);
Vlayout vlayout = new Vlayout();
appendChild(vlayout);
vlayout.appendChild(grid);
grid.setVflex("min");
grid.setHflex("1");
Separator separator = new Separator();
separator.setOrient("horizontal");
vlayout.appendChild(separator);
vlayout.appendChild(confirmPanel);
Button btn = new Button();
btn.setImage("/images/Attachment24.png");
btn.setUpload("true");
btn.addEventListener(Events.ON_UPLOAD, this);
btn.setTooltiptext(Msg.getMsg(Env.getCtx(), "Attachment"));
confirmPanel.addComponentsLeft(btn);
confirmPanel.getButton(ConfirmPanel.A_OK).setWidgetListener("onClick", "zAu.cmd0.showBusy(null)");
addAttachment(FeedbackManager.getLogAttachment(false), false);
}
public void onEvent(Event e) throws Exception {
if (e.getTarget() == confirmPanel.getButton(ConfirmPanel.A_OK)) {
Clients.clearBusy();
// Check Mandatory fields
if (requestTypeField.getValue() == null || requestTypeField.getValue().equals("0"))
throw new WrongValueException(requestTypeField.getComponent(), Msg.translate(Env.getCtx(), "FillMandatory"));
if (priorityField.getValue() == null || priorityField.getValue().equals(""))
throw new WrongValueException(priorityField.getComponent(), Msg.translate(Env.getCtx(), "FillMandatory"));
if (txtSummary.getText() == null || txtSummary.getText().equals(""))
throw new WrongValueException(txtSummary, Msg.translate(Env.getCtx(), "FillMandatory"));
if (salesRepField.getValue() == null || salesRepField.getValue().equals("0"))
throw new WrongValueException(salesRepField.getComponent(), Msg.translate(Env.getCtx(), "FillMandatory"));
Trx trx = Trx.get(Trx.createTrxName("SaveNewRequest"), true);
try {
trx.start();
MRequest request = new MRequest(Env.getCtx(), 0, trx.getTrxName());
request.setAD_Org_ID(Env.getAD_Org_ID(Env.getCtx()));
request.setR_RequestType_ID((Integer) requestTypeField.getValue());
request.setPriority((String) priorityField.getValue());
request.setSummary(txtSummary.getText());
request.setSalesRep_ID((Integer) salesRepField.getValue());
boolean success = request.save();
if (success)
{
MAttachment attachment = null;
for(DataSource ds : attachments)
{
if (attachment == null)
{
attachment = new MAttachment(Env.getCtx(), 0, request.get_TrxName());
attachment.setAD_Table_ID(request.get_Table_ID());
attachment.setRecord_ID(request.get_ID());
}
attachment.addEntry(ds.getName(), IOUtils.toByteArray(ds.getInputStream()));
}
if (attachment != null)
success = attachment.save();
if (success)
success = trx.commit();
}
if (success)
{
FDialog.info(0, null, Msg.getMsg(Env.getCtx(), "Saved"));
}
else
{
trx.rollback();
FDialog.error(0, this, Msg.getMsg(Env.getCtx(), "SaveError"));
}
} finally {
trx.close();
}
this.detach();
}
else if (e.getTarget() == confirmPanel.getButton(ConfirmPanel.A_CANCEL))
{
this.detach();
}
else if (e instanceof UploadEvent)
{
UploadEvent ue = (UploadEvent) e;
Media media = ue.getMedia();
if (media != null)
{
byte[] data = getMediaData(media);
ByteArrayDataSource dataSource = new ByteArrayDataSource(data, media.getContentType());
dataSource.setName(media.getName());
addAttachment(dataSource, true);
getFirstChild().invalidate();
}
}
}
private void addAttachment(DataSource dataSource, boolean removable) {
attachments.add(dataSource);
AttachmentItem item = new AttachmentItem(dataSource, attachments, removable);
attachmentBox.appendChild(item);
}
private byte[] getMediaData(Media media) {
byte[] bytes = null;
try {
if (media.inMemory()) {
bytes = media.isBinary() ? media.getByteData() : media.getStringData().getBytes(getCharset(media.getContentType()));
} else {
InputStream is = media.getStreamData();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[ 1000 ];
int byteread = 0;
while (( byteread=is.read(buf) )!=-1)
baos.write(buf,0,byteread);
bytes = baos.toByteArray();
}
} catch (IOException e) {
log.log(Level.SEVERE, e.getLocalizedMessage(), e);
throw new IllegalStateException(e.getLocalizedMessage());
}
return bytes;
}
private String getCharset(String contentType) {
if (contentType != null) {
int j = contentType.indexOf("charset=");
if (j >= 0) {
String cs = contentType.substring(j + 8).trim();
if (cs.length() > 0) return cs;
}
}
return "UTF-8";
}
}

View File

@ -0,0 +1,60 @@
/******************************************************************************
* Copyright (C) 2013 Heng Sin Low *
* Copyright (C) 2013 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.adempiere.webui.component;
import java.util.List;
import javax.activation.DataSource;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.A;
import org.zkoss.zul.Hlayout;
/**
*
* @author hengsin
*
*/
public class AttachmentItem extends Hlayout implements EventListener<Event>{
/**
* generate serial id
*/
private static final long serialVersionUID = 9105759170502414466L;
private DataSource ds;
private List<DataSource> list;
public AttachmentItem(DataSource ds, List<DataSource> list, boolean removable) {
setStyle("border: 1px solid #dcdcdc; background-color: #f5f5f5; " +
"width: auto !important;display: inline-block; height: 21px; " +
"margin-right: 5px; margin-bottom: 5px;padding-left: 5px; padding-right: 5px;");
appendChild(new Label(ds.getName()));
if (removable) {
A x = new A("", "/images/X8.png");
x.setStyle("float: right; background-color: #f5f5f5");
appendChild(x);
this.ds = ds;
this.list = list;
x.addEventListener(Events.ON_CLICK, this);
}
setHflex("0");
}
@Override
public void onEvent(Event event) throws Exception {
list.remove(ds);
this.detach();
}
}

View File

@ -0,0 +1,77 @@
/******************************************************************************
* Copyright (C) 2013 Heng Sin Low *
* Copyright (C) 2013 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.adempiere.webui.factory;
import javax.activation.DataSource;
import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.apps.FeedbackRequestWindow;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.util.FeedbackManager;
import org.adempiere.webui.window.WEMailDialog;
import org.compiere.model.MSystem;
import org.compiere.model.MUser;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.zkoss.zul.Window.Mode;
/**
* @author hengsin
*
*/
public class DefaultFeedbackService implements IFeedbackService {
/**
* default constructor
*/
public DefaultFeedbackService() {
}
/* (non-Javadoc)
* @see org.adempiere.webui.factory.IFeedbackService#emailSupport(boolean)
*/
@Override
public void emailSupport(boolean errorOnly) {
DataSource ds = FeedbackManager.getLogAttachment(errorOnly);
WEMailDialog dialog = new WEMailDialog(
Msg.getMsg(Env.getCtx(), "EMailSupport"),
MUser.get(Env.getCtx()),
"", // to
"iDempiere " + Msg.getMsg(Env.getCtx(), "TraceInfo"),
"", ds);
dialog.setAttribute(Window.MODE_KEY, Mode.OVERLAPPED);
MSystem system = MSystem.get(Env.getCtx());
if (!Util.isEmpty(system.getSupportEMail()))
{
dialog.addTo(system.getSupportEMail(), true);
}
AEnv.showWindow(dialog);
dialog.focus();
}
/* (non-Javadoc)
* @see org.adempiere.webui.factory.IFeedbackService#createNewRequest()
*/
@Override
public void createNewRequest() {
FeedbackRequestWindow window = new FeedbackRequestWindow();
AEnv.showWindow(window);
window.focus();
}
}

View File

@ -0,0 +1,33 @@
/******************************************************************************
* Copyright (C) 2013 Heng Sin Low *
* Copyright (C) 2013 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.adempiere.webui.factory;
/**
*
* @author hengsin
*
*/
public interface IFeedbackService {
/**
* Email to support
* @param errorOnly
*/
public void emailSupport(boolean errorOnly);
/**
* Create new support request
*/
public void createNewRequest();
}

View File

@ -483,6 +483,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL
else
contentPanel.setStyle("width: 99%; margin: 0px auto;");
contentPanel.setVflex(true);
contentPanel.setSizedByContent(true);
North north = new North();
layout.appendChild(north);

View File

@ -169,6 +169,7 @@ public class InfoGeneralPanel extends InfoPanel implements EventListener<Event>
else
contentPanel.setStyle("width: 99%; margin: 0px auto;");
contentPanel.setVflex(true);
contentPanel.setSizedByContent(true);
div.setStyle("width :100%; height: 100%");
center.appendChild(div);
div.setVflex("1");

View File

@ -21,9 +21,11 @@ import java.util.Properties;
import org.adempiere.webui.LayoutUtils;
import org.adempiere.webui.component.Label;
import org.adempiere.webui.component.Menupopup;
import org.adempiere.webui.component.Messagebox;
import org.adempiere.webui.component.ToolBarButton;
import org.adempiere.webui.session.SessionManager;
import org.adempiere.webui.util.FeedbackManager;
import org.adempiere.webui.window.WPreference;
import org.compiere.model.MClient;
import org.compiere.model.MOrg;
@ -35,6 +37,7 @@ import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Menuitem;
import org.zkoss.zul.Separator;
import org.zkoss.zul.Vbox;
@ -56,9 +59,12 @@ public class UserPanel extends Vbox implements EventListener<Event>
private ToolBarButton logout = new ToolBarButton();
private ToolBarButton changeRole = new ToolBarButton();
private ToolBarButton preference = new ToolBarButton();
private ToolBarButton feedback = new ToolBarButton();
private Label lblUserNameValue = new Label();
private WPreference preferencePopup;
private Menupopup feedbackMenu;
public UserPanel()
{
@ -87,6 +93,18 @@ public class UserPanel extends Vbox implements EventListener<Event>
vbox.appendChild(hbox);
hbox.setAlign("center");
feedback.setLabel(Msg.getMsg(Env.getCtx(), "Feedback"));
feedback.setId("feedback");
feedback.addEventListener(Events.ON_CLICK, this);
LayoutUtils.addSclass("desktop-header-font", feedback);
LayoutUtils.addSclass("link", feedback);
feedback.setParent(hbox);
Separator sep = new Separator("vertical");
sep.setBar(true);
sep.setHeight("13px");
sep.setParent(hbox);
preference.setLabel(Msg.getMsg(Env.getCtx(), "Preference"));
preference.setId("preference");
preference.addEventListener(Events.ON_CLICK, this);
@ -94,7 +112,7 @@ public class UserPanel extends Vbox implements EventListener<Event>
LayoutUtils.addSclass("link", preference);
preference.setParent(hbox);
Separator sep = new Separator("vertical");
sep = new Separator("vertical");
sep.setBar(true);
sep.setHeight("13px");
sep.setParent(hbox);
@ -117,6 +135,16 @@ public class UserPanel extends Vbox implements EventListener<Event>
LayoutUtils.addSclass("desktop-header-font", logout);
LayoutUtils.addSclass("link", logout);
logout.setParent(hbox);
feedbackMenu = new Menupopup();
Menuitem mi = new Menuitem(Msg.getMsg(Env.getCtx(), "RequestNew"));
mi.setId("CreateRequest");
feedbackMenu.appendChild(mi);
mi.addEventListener(Events.ON_CLICK, this);
mi = new Menuitem(Msg.getMsg(Env.getCtx(), "EMailSupport"));
mi.setId("EmailSupport");
mi.addEventListener(Events.ON_CLICK, this);
feedbackMenu.appendChild(mi);
}
private String getUserName()
@ -180,6 +208,26 @@ public class UserPanel extends Vbox implements EventListener<Event>
preferencePopup.setPage(this.getPage());
preferencePopup.open(preference, "after_start");
}
else if (feedback == event.getTarget())
{
if (feedbackMenu.getPage() == null)
{
this.appendChild(feedbackMenu);
}
feedbackMenu.open(feedback, "after_start");
}
else if (event.getTarget() instanceof Menuitem)
{
Menuitem mi = (Menuitem) event.getTarget();
if ("CreateRequest".equals(mi.getId()))
{
FeedbackManager.createNewRequest();
}
else if ("EmailSupport".equals(mi.getId()))
{
FeedbackManager.emailSupport(false);
}
}
}
}

View File

@ -115,7 +115,7 @@ public class WAttachment extends Window implements EventListener<Event>
autoPreviewList.add("image/jpeg");
autoPreviewList.add("image/png");
autoPreviewList.add("image/gif");
autoPreviewList.add("text/plan");
autoPreviewList.add("text/plain");
autoPreviewList.add("application/pdf");
}

View File

@ -0,0 +1,69 @@
/******************************************************************************
* Copyright (C) 2013 Heng Sin Low *
* Copyright (C) 2013 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.adempiere.webui.util;
import javax.activation.DataSource;
import org.adempiere.base.Service;
import org.adempiere.webui.ClientInfo;
import org.adempiere.webui.factory.IFeedbackService;
import org.adempiere.webui.session.SessionManager;
import org.compiere.util.ByteArrayDataSource;
import org.compiere.util.CLogErrorBuffer;
import org.compiere.util.Env;
/**
* @author hengsin
*
*/
public class FeedbackManager {
/**
*
* @param errorOnly
* @return attachment datasource
*/
public static DataSource getLogAttachment(boolean errorOnly)
{
String context = CLogErrorBuffer.get(true).getErrorInfo(Env.getCtx(), errorOnly);
ClientInfo browserInfo = SessionManager.getAppDesktop().getClientInfo();
StringBuilder info = new StringBuilder(browserInfo.toString());
info.append("\r\n").append(context);
ByteArrayDataSource ds = new ByteArrayDataSource(info.toString(), "UTF-8", "text/plain");
ds.setName("idempiere-log.txt");
return ds;
}
/**
* EMail Support
*/
public static void emailSupport(boolean errorOnly)
{
IFeedbackService service = Service.locator().locate(IFeedbackService.class).getService();
if (service != null)
service.emailSupport(errorOnly);
}
/**
* Create new support request
*/
public static void createNewRequest()
{
IFeedbackService service = Service.locator().locate(IFeedbackService.class).getService();
if (service != null)
service.createNewRequest();
}
}

View File

@ -36,6 +36,7 @@ import org.adempiere.webui.component.Tabs;
import org.adempiere.webui.component.ToolBarButton;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.util.FeedbackManager;
import org.compiere.Adempiere;
import org.compiere.model.MUser;
import org.compiere.util.CLogErrorBuffer;
@ -550,13 +551,7 @@ public class AboutWindow extends Window implements EventListener<Event> {
*/
private void cmd_errorEMail()
{
new WEMailDialog(this,
"EMail Trace",
MUser.get(Env.getCtx()),
"", // to
"Adempiere Trace Info",
CLogErrorBuffer.get(true).getErrorInfo(Env.getCtx(), bErrorsOnly.isSelected()),
null);
this.detach();
FeedbackManager.emailSupport(bErrorsOnly.isSelected());
} // cmd_errorEMail
}

View File

@ -18,11 +18,18 @@
package org.adempiere.webui.window;
import java.beans.PropertyVetoException;
import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Level;
import org.adempiere.webui.apps.AEnv;
import javax.activation.DataSource;
import org.adempiere.webui.component.AttachmentItem;
import org.adempiere.webui.component.Button;
import org.adempiere.webui.component.Column;
import org.adempiere.webui.component.Columns;
import org.adempiere.webui.component.ConfirmPanel;
@ -35,24 +42,28 @@ import org.adempiere.webui.component.Window;
import org.adempiere.webui.editor.WSearchEditor;
import org.adempiere.webui.event.ValueChangeEvent;
import org.adempiere.webui.event.ValueChangeListener;
import org.adempiere.webui.panel.StatusBarPanel;
import org.compiere.model.Lookup;
import org.compiere.model.MClient;
import org.compiere.model.MLookupFactory;
import org.compiere.model.MUser;
import org.compiere.model.MUserMail;
import org.compiere.util.ByteArrayDataSource;
import org.compiere.util.CLogger;
import org.compiere.util.DisplayType;
import org.compiere.util.EMail;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.zkoss.util.media.Media;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Borderlayout;
import org.zkoss.zul.Center;
import org.zkoss.zul.South;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.UploadEvent;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Cell;
import org.zkoss.zul.Div;
import org.zkoss.zul.Separator;
import org.zkoss.zul.Vlayout;
/**
* EMail Dialog
@ -77,7 +88,6 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
/**
* EMail Dialog
* @param owner calling window
* @param title title
* @param from from
* @param to to
@ -85,16 +95,17 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
* @param message message
* @param attachment optional attachment
*/
public WEMailDialog (Window owner, String title, MUser from, String to,
String subject, String message, File attachment)
public WEMailDialog (String title, MUser from, String to,
String subject, String message, DataSource attachment)
{
super();
this.setTitle(title);
this.setWidth("500px");
this.setWidth("550px");
this.setHeight("600px");
this.setClosable(true);
this.setMaximizable(true);
this.setBorder("normal");
this.setStyle("position:absolute");
this.setStyle("position:absolute; margin: 0; padding: 0;");
commonInit(from, to, subject, message, attachment);
} // EmailDialog
@ -108,7 +119,7 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
* @param attachment optional attachment
*/
private void commonInit (MUser from, String to,
String subject, String message, File attachment)
String subject, String message, DataSource attachment)
{
m_client = MClient.get(Env.getCtx());
try
@ -124,7 +135,6 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
fUser.addValueChangeListener(this);
fCcUser = new WSearchEditor(lookup, "AD_User_ID", "", false, false, true);
fCcUser.addValueChangeListener(this);
jbInit();
}
catch(Exception ex)
{
@ -133,7 +143,6 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
set(from, to, subject, message);
setAttachment(attachment);
setAttribute(Window.MODE_KEY, Window.MODE_HIGHLIGHTED);
AEnv.showWindow(this);
} // commonInit
@ -151,7 +160,10 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
private String m_subject;
private String m_message;
/** File to be optionally attached */
private File m_attachFile;
private DataSource m_attachment;
private List<DataSource> attachments = new ArrayList<DataSource>();
/** Logger */
private static CLogger log = CLogger.getCLogger(WEMailDialog.class);
@ -166,15 +178,24 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
private Label lCc = new Label();
private Label lSubject = new Label();
private Label lAttachment = new Label();
private Textbox fAttachment = new Textbox();//40);
private Textbox fMessage = new Textbox();
private ConfirmPanel confirmPanel = new ConfirmPanel(true);
private StatusBarPanel statusBar = new StatusBarPanel();
private Div attachmentBox;
@Override
public void onPageAttached(Page newpage, Page oldpage) {
super.onPageAttached(newpage, oldpage);
try {
render();
} catch (Exception e) {
}
}
/**
* Static Init
*/
void jbInit() throws Exception
protected void render() throws Exception
{
lFrom.setValue(Msg.getMsg(Env.getCtx(), "From") + ":");
lTo.setValue(Msg.getMsg(Env.getCtx(), "To") + ":");
@ -182,13 +203,11 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
lSubject.setValue(Msg.getMsg(Env.getCtx(), "Subject") + ":");
lAttachment.setValue(Msg.getMsg(Env.getCtx(), "Attachment") + ":");
fFrom.setReadonly(true);
statusBar.setStatusDB(null);
//
Grid grid = new Grid();
grid.setWidth("100%");
grid.setHeight("100%");
grid.setStyle("margin:0; padding:0; position: absolute; align: center; valign: center; border:0");
grid.setStyle("margin:0; padding:0; align: center; valign: center; border:0");
grid.makeNoStrip();
Columns columns = new Columns();
@ -241,11 +260,7 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
row.appendChild(new Label(""));
row.appendChild(fCc);
fCc.setHflex("1");
row = new Row();
rows.appendChild(row);
row.appendCellChild(new Separator(), 2);
row = new Row();
rows.appendChild(row);
div = new Div();
@ -255,18 +270,29 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
row.appendChild(fSubject);
fSubject.setHflex("1");
row = new Row();
rows.appendChild(row);
row.appendCellChild(new Separator(), 2);
row = new Row();
rows.appendChild(row);
div = new Div();
div.setStyle("text-align: right;");
div.appendChild(lAttachment);
row.appendChild(div);
row.appendChild(fAttachment);
fAttachment.setHflex("1");
Cell cell = new Cell();
cell.appendChild(lAttachment);
cell.setValign("top");
cell.setAlign("right");
row.appendChild(cell);
attachmentBox = new Div();
attachmentBox.setHflex("1");
attachmentBox.setVflex("1");
row.appendChild(attachmentBox);
for (DataSource ds : attachments) {
boolean removable = true;
if (ds == m_attachment) {
removable = false;
}
AttachmentItem item = new AttachmentItem(ds, attachments, removable);
attachmentBox.appendChild(item);
}
row = new Row();
rows.appendChild(row);
@ -276,29 +302,28 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
confirmPanel.addActionListener(this);
Borderlayout layout = new Borderlayout();
layout.setWidth("95%");
layout.setHeight("92%");
layout.setStyle("background-color: white; position: absolute; margin:0; border:0; padding:0");
Vlayout vlayout = new Vlayout();
vlayout.setStyle("width: 99%; margin: auto; height: 100%;");
Center center = new Center();
grid.setHflex("true");
grid.setVflex("true");
center.appendChild(grid);
layout.appendChild(center);
center.setStyle("background-color: white; border: 0");
grid.setVflex("1");
vlayout.appendChild(grid);
South south = new South();
Div southDiv = new Div();
south.appendChild(southDiv);
layout.appendChild(south);
south.setStyle("background-color: white; border: 0");
Button btn = new Button();
btn.setImage("/images/Attachment24.png");
btn.setUpload("true");
btn.addEventListener(Events.ON_UPLOAD, this);
btn.setTooltiptext(Msg.getMsg(Env.getCtx(), "Attachment"));
confirmPanel.addComponentsLeft(btn);
confirmPanel.getButton(ConfirmPanel.A_OK).setWidgetListener("onClick", "zAu.cmd0.showBusy(null)");
southDiv.appendChild(confirmPanel);
southDiv.appendChild(statusBar);
southDiv.setVflex("min");
vlayout.appendChild(southDiv);
this.appendChild(layout);
} // jbInit
this.appendChild(vlayout);
} // render
/**
* Set all properties
@ -310,8 +335,6 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
setTo(to);
setSubject(subject);
setMessage(message);
//
statusBar.setStatusLine(m_client.getSMTPHost());
} // set
/**
@ -360,8 +383,7 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
|| !newFrom.isEMailValid()
|| !newFrom.isCanSendEMail())
{
// confirmPanel.getOKButton().setEnabled(false);
fFrom.setText("**Invalid**");
fFrom.setText("");
}
else
fFrom.setText(m_from.getEMail());
@ -415,29 +437,19 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
/**
* Set Attachment
*/
public void setAttachment (File attachment)
public void setAttachment (DataSource attachment)
{
m_attachFile = attachment;
if (attachment == null)
{
lAttachment.setVisible(false);
fAttachment.setVisible(false);
}
else
{
lAttachment.setVisible(true);
fAttachment.setVisible(true);
fAttachment.setText(attachment.getName());
fAttachment.setReadonly(true);
}
m_attachment = attachment;
if (attachment != null)
attachments.add(attachment);
} // setAttachment
/**
* Get Attachment
*/
public File getAttachment()
public DataSource getAttachment()
{
return m_attachFile;
return m_attachment;
} // getAttachment
/**************************************************************************
@ -446,14 +458,15 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
public void onEvent(Event event) throws Exception {
if (event.getTarget().getId().equals(ConfirmPanel.A_CANCEL))
onClose();
if (getTo() == null || getTo().length() == 0)
{
return;
}
// Send
if (event.getTarget().getId().equals(ConfirmPanel.A_OK))
else if (event.getTarget().getId().equals(ConfirmPanel.A_OK))
{
Clients.clearBusy();
if (getTo() == null || getTo().length() == 0)
{
return;
}
StringTokenizer st = new StringTokenizer(getTo(), " ,;", false);
String to = st.nextToken();
@ -472,8 +485,11 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
email.addCc(cc);
}
// Attachment
if (m_attachFile != null && m_attachFile.exists())
email.addAttachment(m_attachFile);
for(DataSource ds : attachments)
{
email.addAttachment(ds);
}
status = email.send();
//
if (m_user != null)
@ -488,14 +504,61 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
}
else
FDialog.error(0, this, "MessageNotSent", status);
//
// confirmPanel.getOKButton().setEnabled(false);
// setCursor(Cursor.getDefaultCursor());
}
else if (event.getTarget().getId().equals(ConfirmPanel.A_CANCEL))
onClose();
else if (event instanceof UploadEvent)
{
UploadEvent ue = (UploadEvent) event;
Media media = ue.getMedia();
if (media != null)
{
byte[] data = getMediaData(media);
ByteArrayDataSource dataSource = new ByteArrayDataSource(data, media.getContentType());
dataSource.setName(media.getName());
attachments.add(dataSource);
AttachmentItem item = new AttachmentItem(dataSource, attachments, true);
attachmentBox.appendChild(item);
getFirstChild().invalidate();
}
}
}
private byte[] getMediaData(Media media) {
byte[] bytes = null;
try {
if (media.inMemory()) {
bytes = media.isBinary() ? media.getByteData() : media.getStringData().getBytes(getCharset(media.getContentType()));
} else {
InputStream is = media.getStreamData();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[ 1000 ];
int byteread = 0;
while (( byteread=is.read(buf) )!=-1)
baos.write(buf,0,byteread);
bytes = baos.toByteArray();
}
} catch (IOException e) {
log.log(Level.SEVERE, e.getLocalizedMessage(), e);
throw new IllegalStateException(e.getLocalizedMessage());
}
return bytes;
}
private String getCharset(String contentType) {
if (contentType != null) {
int j = contentType.indexOf("charset=");
if (j >= 0) {
String cs = contentType.substring(j + 8).trim();
if (cs.length() > 0) return cs;
}
}
return "UTF-8";
}
/**
* Vetoable Change - User selected
* @param evt
@ -518,7 +581,14 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
{
int AD_User_ID = ((Integer)value).intValue();
m_user = MUser.get(Env.getCtx(), AD_User_ID);
fTo.setValue(m_user.getEMail());
if (Util.isEmpty(m_user.getEMail()))
{
FDialog.error(0, Msg.getMsg(Env.getCtx(), "UserNoEmailAddress"));
}
else
{
addTo(m_user.getEMail(), true);
}
}
} else {
// fCcUser
@ -526,11 +596,41 @@ public class WEMailDialog extends Window implements EventListener<Event>, ValueC
{
int AD_User_ID = ((Integer)value).intValue();
m_ccuser = MUser.get(Env.getCtx(), AD_User_ID);
fCc.setValue(m_ccuser.getEMail());
if (Util.isEmpty(m_ccuser.getEMail()))
{
FDialog.error(0, Msg.getMsg(Env.getCtx(), "UserNoEmailAddress"));
}
else
{
addCC(m_ccuser.getEMail(), true);
}
}
}
return;
}
public void addTo(String email, boolean first) {
if (Util.isEmpty(email))
return;
String to = fTo.getValue();
if (!Util.isEmpty(to)) {
fTo.setValue(first ? email+","+to : to+","+email);
} else {
fTo.setValue(email);
}
}
public void addCC(String email, boolean first) {
if (Util.isEmpty(email))
return;
String to = fCc.getValue();
if (!Util.isEmpty(to)) {
fCc.setValue(first ? email+","+to : to+","+email);
} else {
fCc.setValue(email);
}
}
} // VEMailDialog

View File

@ -27,6 +27,7 @@ import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Level;
import javax.activation.FileDataSource;
import javax.servlet.http.HttpServletRequest;
import org.adempiere.exceptions.AdempiereException;
@ -775,9 +776,9 @@ public class ZkReportViewer extends Window implements EventListener<Event>, ITab
log.log(Level.SEVERE, "", e);
}
new WEMailDialog (this,
Msg.getMsg(Env.getCtx(), "SendMail"),
from, to, subject, message, attachment);
WEMailDialog dialog = new WEMailDialog (Msg.getMsg(Env.getCtx(), "SendMail"),
from, to, subject, message, new FileDataSource(attachment));
AEnv.showWindow(dialog);
} // cmd_sendMail
/**

View File

@ -29,7 +29,8 @@ bin.includes = META-INF/,\
WEB-INF/lib/atmosphere-compat-tomcat7-1.0.4.jar,\
WEB-INF/lib/atmosphere-runtime-1.0.4.jar,\
OSGI-INF/defaultcreatefromfactory.xml,\
OSGI-INF/defaultformfactory.xml
OSGI-INF/defaultformfactory.xml,\
OSGI-INF/feedbackservice.xml
src.includes = WEB-INF/classes/,\
WEB-INF/tld/,\
WEB-INF/web.xml,\

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B