Skip to content
Browse files

Beginnings of recipient management; attendant fixes and tweaks

  • Loading branch information...
1 parent afb4f7d commit 1712345123420ab1a7003af987bc012840bec71d @cqcallaw committed May 13, 2012
Showing with 944 additions and 158 deletions.
  1. +51 −0 src/net/brainvitamins/timeout/client/EmailRecipientProxy.java
  2. +66 −10 src/net/brainvitamins/timeout/client/Timeout.java
  3. +78 −0 src/net/brainvitamins/timeout/client/editors/EmailRecipientDialog.java
  4. +12 −0 src/net/brainvitamins/timeout/client/editors/EmailRecipientDialog.ui.xml
  5. +46 −0 src/net/brainvitamins/timeout/client/editors/EmailRecipientEditor.java
  6. +24 −0 src/net/brainvitamins/timeout/client/editors/EmailRecipientEditor.ui.xml
  7. +18 −0 src/net/brainvitamins/timeout/client/services/RecipientService.java
  8. +16 −0 src/net/brainvitamins/timeout/client/services/RecipientServiceAsync.java
  9. +10 −10 src/net/brainvitamins/timeout/client/views/ActivityView.java
  10. +8 −0 src/net/brainvitamins/timeout/client/views/CellTableView.java
  11. +0 −17 src/net/brainvitamins/timeout/client/views/Main.ui.xml
  12. +23 −14 src/net/brainvitamins/timeout/client/views/{Main.java → MainView.java}
  13. +26 −0 src/net/brainvitamins/timeout/client/views/MainView.ui.xml
  14. +184 −0 src/net/brainvitamins/timeout/client/views/RecipientListView.java
  15. +12 −0 src/net/brainvitamins/timeout/client/views/RecipientListView.ui.xml
  16. +113 −0 src/net/brainvitamins/timeout/client/views/RemoveCell.java
  17. +3 −17 src/net/brainvitamins/timeout/server/ActivityLogger.java
  18. +13 −6 src/net/brainvitamins/timeout/server/ActivityServiceImpl.java
  19. +1 −0 src/net/brainvitamins/timeout/server/CheckinServiceImpl.java
  20. +62 −23 src/net/brainvitamins/timeout/server/DataOperations.java
  21. +1 −13 src/net/brainvitamins/timeout/server/LoginServiceImpl.java
  22. +67 −31 src/net/brainvitamins/timeout/server/RecipientServiceImpl.java
  23. +2 −0 src/net/brainvitamins/timeout/server/TimeoutServlet.java
  24. +47 −8 src/net/brainvitamins/timeout/shared/EmailRecipient.java
  25. +37 −6 src/net/brainvitamins/timeout/shared/Recipient.java
  26. +7 −3 src/net/brainvitamins/timeout/shared/User.java
  27. +1 −0 war/.gitignore
  28. +10 −0 war/WEB-INF/web.xml
  29. +6 −0 war/stylesheets/main.css
View
51 src/net/brainvitamins/timeout/client/EmailRecipientProxy.java
@@ -0,0 +1,51 @@
+package net.brainvitamins.timeout.client;
+
+import net.brainvitamins.timeout.shared.EmailRecipient;
+
+/*
+ * Mutable proxy class for EmailRecipients, for Editor support
+ */
+public class EmailRecipientProxy
+{
+ private EmailRecipient result;
+
+ /**
+ * @return the result
+ */
+ public EmailRecipient getResult()
+ {
+ return result;
+ }
+
+ public String getName()
+ {
+ return result.getName();
+ }
+
+ public void setName(String name)
+ {
+ result = new EmailRecipient(name, result.isVerified(),
+ result.getAddress());
+ }
+
+ public String getAddress()
+ {
+ return result.getName();
+ }
+
+ public void setAddress(String address)
+ {
+ result = new EmailRecipient(result.getName(), result.isVerified(),
+ address);
+ }
+
+ public EmailRecipientProxy()
+ {
+ this(new EmailRecipient());
+ }
+
+ public EmailRecipientProxy(EmailRecipient result)
+ {
+ this.result = result;
+ }
+}
View
76 src/net/brainvitamins/timeout/client/Timeout.java
@@ -8,18 +8,19 @@
import net.brainvitamins.timeout.client.services.ActivityServiceAsync;
import net.brainvitamins.timeout.client.services.LoginService;
import net.brainvitamins.timeout.client.services.LoginServiceAsync;
-import net.brainvitamins.timeout.client.views.Main;
-import net.brainvitamins.timeout.shared.Checkin;
+import net.brainvitamins.timeout.client.services.RecipientService;
+import net.brainvitamins.timeout.client.services.RecipientServiceAsync;
+import net.brainvitamins.timeout.client.views.MainView;
import net.brainvitamins.timeout.shared.Activity;
import net.brainvitamins.timeout.shared.LoginInfo;
+import net.brainvitamins.timeout.shared.Recipient;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.shared.DefaultDateTimeFormatInfo;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -51,6 +52,9 @@
private ActivityServiceAsync activityService = GWT
.create(ActivityService.class);
+ private RecipientServiceAsync recipientService = GWT
+ .create(RecipientService.class);
+
private static Logger logger = Logger.getLogger("Main");
/**
@@ -106,16 +110,30 @@ private void loadMain(LoginInfo loginInfo)
dateFormatInfo.dateFormatShort());
final ListDataProvider<Activity> activityDataProvider = new ListDataProvider<Activity>();
- final Main homeView = new Main(dateFormat, activityDataProvider);
+ final ListDataProvider<Recipient> recipientDataProvider = new ListDataProvider<Recipient>();
+
+ // final MainView homeView = new MainView(dateFormat,
+ // activityDataProvider, recipientDataProvider);
+
+ // ugh, so wrong and backwards to initialize a view without having data
+ // attached...
+ // visual layout editing fails otherwise, though
+ // need a wrapper class of some kind
+ final MainView homeView = new MainView(dateFormat);
+ activityDataProvider.addDataDisplay(homeView.getActivityView()
+ .getCellView());
+ recipientDataProvider.addDataDisplay(homeView.getRecipientView()
+ .getCellView());
RootPanel.get("content").add(homeView);
refreshActivity(activityDataProvider);
+ refreshRecipients(recipientDataProvider);
+
logger.log(Level.INFO, "App loaded.");
-
- // refresh
- Timer refreshTimer = new Timer()
+
+ Timer activityRefreshTimer = new Timer()
{
@Override
public void run()
@@ -124,7 +142,46 @@ public void run()
}
};
- refreshTimer.scheduleRepeating(REFRESH_INTERVAL);
+ // TODO: jitter the intervals
+ activityRefreshTimer.scheduleRepeating(REFRESH_INTERVAL);
+
+ Timer recipientRefreshTimer = new Timer()
+ {
+ @Override
+ public void run()
+ {
+ // TODO: locking so the list doesn't get hammered by multiple
+ // updates
+ refreshRecipients(recipientDataProvider);
+ }
+ };
+
+ recipientRefreshTimer.scheduleRepeating(REFRESH_INTERVAL);
+ }
+
+ private void refreshRecipients(
+ final ListDataProvider<Recipient> dataProvider)
+ {
+ recipientService.getRecipients(new AsyncCallback<List<Recipient>>()
+ {
+
+ @Override
+ public void onSuccess(List<Recipient> result)
+ {
+ List<Recipient> list = dataProvider.getList();
+ list.clear();
+ for (Recipient activity : result)
+ {
+ list.add(activity);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable caught)
+ {
+ // TODO:
+ }
+ });
}
/**
@@ -138,8 +195,7 @@ private void refreshActivity(final ListDataProvider<Activity> dataProvider)
@Override
public void onFailure(Throwable caught)
{
- // TODO Auto-generated method stub
-
+ // TODO:
}
@Override
View
78 src/net/brainvitamins/timeout/client/editors/EmailRecipientDialog.java
@@ -0,0 +1,78 @@
+package net.brainvitamins.timeout.client.editors;
+
+import net.brainvitamins.timeout.client.EmailRecipientProxy;
+import net.brainvitamins.timeout.shared.EmailRecipient;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.editor.client.SimpleBeanEditorDriver;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.DialogBox;
+import com.google.gwt.user.client.ui.Widget;
+
+public class EmailRecipientDialog extends DialogBox
+{
+ interface EmailRecipientViewUiBinder extends
+ UiBinder<Widget, EmailRecipientDialog>
+ {
+ }
+
+ private static EmailRecipientViewUiBinder uiBinder = GWT
+ .create(EmailRecipientViewUiBinder.class);
+
+ interface Driver extends
+ SimpleBeanEditorDriver<EmailRecipientProxy, EmailRecipientEditor>
+ {
+ }
+
+ private Driver driver;
+
+ // /**
+ // * @return the driver
+ // */
+ // public Driver getDriver()
+ // {
+ // return driver;
+ // }
+ //
+ private EmailRecipient editResult;
+
+ /**
+ * @return the edit result (will be identical to the source object if no
+ * changes have been made)
+ */
+ public EmailRecipient getEditResult()
+ {
+ return editResult;
+ }
+
+ @UiField(provided = true)
+ EmailRecipientEditor editor;
+
+ public EmailRecipientDialog()
+ {
+ editor = new EmailRecipientEditor();
+ driver = GWT.create(Driver.class);
+ driver.initialize(editor);
+
+ setWidget(uiBinder.createAndBindUi(this));
+ }
+
+ public void edit(EmailRecipient recipient)
+ {
+ driver.edit(new EmailRecipientProxy(recipient));
+ this.center();
+ }
+
+ @UiHandler("okButton")
+ void save(ClickEvent event)
+ {
+ editResult = driver.flush().getResult();
+
+ // TODO: handle errors
+
+ this.hide();
+ }
+}
View
12 src/net/brainvitamins/timeout/client/editors/EmailRecipientDialog.ui.xml
@@ -0,0 +1,12 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:e="urn:import:net.brainvitamins.timeout.client.editors">
+ <g:VerticalPanel horizontalAlignment="ALIGN_LEFT">
+ <e:EmailRecipientEditor ui:field="editor" />
+ <g:Cell horizontalAlignment="ALIGN_RIGHT">
+ <g:VerticalPanel horizontalAlignment="ALIGN_RIGHT">
+ <g:Button ui:field="okButton" text="OK" styleName="okButton" />
+ </g:VerticalPanel>
+ </g:Cell>
+ </g:VerticalPanel>
+</ui:UiBinder>
View
46 src/net/brainvitamins/timeout/client/editors/EmailRecipientEditor.java
@@ -0,0 +1,46 @@
+package net.brainvitamins.timeout.client.editors;
+
+import net.brainvitamins.timeout.client.EmailRecipientProxy;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.editor.client.Editor;
+import com.google.gwt.editor.client.SimpleBeanEditorDriver;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+
+public class EmailRecipientEditor extends Composite implements
+ Editor<EmailRecipientProxy>
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 7451050279366185561L;
+
+ private static Binder uiBinder = GWT
+ .create(Binder.class);
+
+ interface Binder extends
+ UiBinder<Widget, EmailRecipientEditor>
+ {
+ }
+
+ public interface Driver extends
+ SimpleBeanEditorDriver<EmailRecipientProxy, EmailRecipientEditor>
+ {
+ };
+
+ @UiField
+ TextBox nameEditor = new TextBox();
+
+ @UiField
+ TextBox addressEditor = new TextBox();
+
+ public EmailRecipientEditor()
+ {
+ initWidget(uiBinder.createAndBindUi(this));
+ }
+}
View
24 src/net/brainvitamins/timeout/client/editors/EmailRecipientEditor.ui.xml
@@ -0,0 +1,24 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:c="urn:import:com.google.gwt.user.cellview.client">
+ <g:HTMLPanel>
+ <table>
+ <tr>
+ <td>
+ <g:Label text="Name:" />
+ </td>
+ <td>
+ <g:TextBox ui:field="nameEditor" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <g:Label text="Address:" />
+ </td>
+ <td>
+ <g:TextBox ui:field="addressEditor" />
+ </td>
+ </tr>
+ </table>
+ </g:HTMLPanel>
+</ui:UiBinder>
View
18 src/net/brainvitamins/timeout/client/services/RecipientService.java
@@ -0,0 +1,18 @@
+package net.brainvitamins.timeout.client.services;
+
+
+import java.util.List;
+
+import net.brainvitamins.timeout.shared.Recipient;
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+@RemoteServiceRelativePath("recipient")
+public interface RecipientService extends RemoteService
+{
+ public void addRecipient(Recipient recipient);
+
+ public boolean removeRecipient(Recipient recipient);
+
+ public List<Recipient> getRecipients();
+}
View
16 src/net/brainvitamins/timeout/client/services/RecipientServiceAsync.java
@@ -0,0 +1,16 @@
+package net.brainvitamins.timeout.client.services;
+
+import java.util.List;
+
+import net.brainvitamins.timeout.shared.Recipient;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+public interface RecipientServiceAsync
+{
+ void addRecipient(Recipient recipient, AsyncCallback<Void> callback);
+
+ void getRecipients(AsyncCallback<List<Recipient>> callback);
+
+ void removeRecipient(Recipient recipient, AsyncCallback<Boolean> callback);
+}
View
20 src/net/brainvitamins/timeout/client/views/ActivityView.java
@@ -4,6 +4,7 @@
import net.brainvitamins.timeout.shared.Checkin;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
@@ -12,9 +13,8 @@
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.view.client.ListDataProvider;
-public class ActivityView extends Composite
+public class ActivityView extends Composite implements CellTableView<Activity>
{
private static ActivityUiBinder uiBinder = GWT
@@ -27,7 +27,7 @@
@UiField(provided = true)
final CellTable<Activity> activityView;
- public CellTable<Activity> getActivityView()
+ public CellTable<Activity> getCellView()
{
return activityView;
}
@@ -39,16 +39,13 @@ public String getDateFormat()
return dateFormat;
}
- public ActivityView(final String dateFormat,
- ListDataProvider<Activity> dataProvider)
+ //TODO: date column sorting (client-side)
+ public ActivityView(final String dateFormat)
{
if (dateFormat == null || dateFormat.isEmpty())
throw new IllegalArgumentException(
"dateFormat cannot be empty or null.");
- if (dataProvider == null)
- throw new IllegalArgumentException("dataProvider cannot be null.");
-
this.dateFormat = dateFormat;
activityView = new CellTable<Activity>();
@@ -96,8 +93,11 @@ public String getValue(Activity activity)
activityView.addColumn(typeColumn);
activityView.addColumn(timeoutColumn);
- dataProvider.addDataDisplay(activityView);
-
+ activityView.setWidth("100%", true);
+ activityView.setColumnWidth(timestampColumn, 12, Unit.EM);
+ activityView.setColumnWidth(typeColumn, 6, Unit.EM);
+ activityView.setColumnWidth(timeoutColumn, 100, Unit.PCT);
+
initWidget(uiBinder.createAndBindUi(this));
}
}
View
8 src/net/brainvitamins/timeout/client/views/CellTableView.java
@@ -0,0 +1,8 @@
+package net.brainvitamins.timeout.client.views;
+
+import com.google.gwt.user.cellview.client.CellTable;
+
+public interface CellTableView<T>
+{
+ public abstract CellTable<T> getCellView();
+}
View
17 src/net/brainvitamins/timeout/client/views/Main.ui.xml
@@ -1,17 +0,0 @@
-<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
-<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
- xmlns:v="urn:import:net.brainvitamins.timeout.client.views" xmlns:g='urn:import:com.google.gwt.user.client.ui'>
- <g:HTMLPanel>
- <div id="header">
- <div id="user"></div>
- </div>
-
- <h1>Recent Activity</h1>
- <v:ActivityView ui:field="activityView" />
-
- <h1>Recipients</h1>
- <div id="notified"></div>
-
- <v:CheckinView ui:field="checkinView" />
- </g:HTMLPanel>
-</ui:UiBinder>
View
37 ...invitamins/timeout/client/views/Main.java → ...tamins/timeout/client/views/MainView.java
@@ -1,29 +1,43 @@
package net.brainvitamins.timeout.client.views;
-import net.brainvitamins.timeout.shared.Activity;
-
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.view.client.ListDataProvider;
-public class Main extends Composite
+public class MainView extends Composite
{
private static HomeUiBinder uiBinder = GWT.create(HomeUiBinder.class);
- interface HomeUiBinder extends UiBinder<Widget, Main>
+ interface HomeUiBinder extends UiBinder<Widget, MainView>
{
}
- private ListDataProvider<Activity> activityDataProvider;
-
@UiField
ActivityView activityView;
+ /**
+ * @return the activityView
+ */
+ public ActivityView getActivityView()
+ {
+ return activityView;
+ }
+
+ @UiField
+ RecipientListView recipientView;
+
+ /**
+ * @return the recipientView
+ */
+ public RecipientListView getRecipientView()
+ {
+ return recipientView;
+ }
+
@UiField
CheckinView checkinView = new CheckinView();
@@ -34,21 +48,16 @@ public String getDateFormat()
return dateFormat;
}
- public Main(final String dateFormat,
- ListDataProvider<Activity> activityDataProvider)
+ public MainView(final String dateFormat)
{
this.dateFormat = dateFormat;
- this.activityDataProvider = activityDataProvider;
initWidget(uiBinder.createAndBindUi(this));
}
@UiFactory
protected ActivityView getActivity()
{
- if (activityDataProvider == null)
- throw new IllegalStateException(
- "Cannot initialize activity without activity data provider.");
- return new ActivityView(dateFormat, activityDataProvider);
+ return new ActivityView(dateFormat);
}
}
View
26 src/net/brainvitamins/timeout/client/views/MainView.ui.xml
@@ -0,0 +1,26 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:v="urn:import:net.brainvitamins.timeout.client.views" xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+ <g:VerticalPanel width="100%">
+ <g:HTMLPanel width="100%">
+ <div id="header">
+ <div id="user" />
+ </div>
+ </g:HTMLPanel>
+ <g:VerticalPanel width="100%">
+ <g:HTMLPanel>
+ <h1>Recent Activity</h1>
+ </g:HTMLPanel>
+ <v:ActivityView ui:field="activityView" width="100%" />
+ <g:HTMLPanel>
+ <h1>Recipients</h1>
+ </g:HTMLPanel>
+ <v:RecipientListView ui:field="recipientView"
+ width="100%" />
+ <g:HTMLPanel>
+ <h1>Checkin</h1>
+ </g:HTMLPanel>
+ <v:CheckinView ui:field="checkinView" width="100%" />
+ </g:VerticalPanel>
+ </g:VerticalPanel>
+</ui:UiBinder>
View
184 src/net/brainvitamins/timeout/client/views/RecipientListView.java
@@ -0,0 +1,184 @@
+package net.brainvitamins.timeout.client.views;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.brainvitamins.timeout.client.editors.EmailRecipientDialog;
+import net.brainvitamins.timeout.client.services.RecipientService;
+import net.brainvitamins.timeout.client.services.RecipientServiceAsync;
+import net.brainvitamins.timeout.shared.EmailRecipient;
+import net.brainvitamins.timeout.shared.Recipient;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.cellview.client.CellTable;
+import com.google.gwt.user.cellview.client.Column;
+import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
+import com.google.gwt.user.cellview.client.TextColumn;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+public class RecipientListView extends Composite implements
+ CellTableView<Recipient>
+{
+
+ private static ActivityUiBinder uiBinder = GWT
+ .create(ActivityUiBinder.class);
+
+ interface ActivityUiBinder extends UiBinder<Widget, RecipientListView>
+ {
+ }
+
+ private RecipientServiceAsync recipientService = GWT
+ .create(RecipientService.class);
+
+ @UiField(provided = true)
+ final CellTable<Recipient> recipientView;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.brainvitamins.timeout.client.views.CellTableView#getCellView()
+ */
+ @Override
+ public CellTable<Recipient> getCellView()
+ {
+ return recipientView;
+ }
+
+ @UiField
+ Button addButton;
+
+ private static Logger logger = Logger.getLogger("RecipientList");
+
+ public RecipientListView()
+ {
+ recipientView = new CellTable<Recipient>();
+
+ // ref: http://stackoverflow.com/q/10454435/577298
+ recipientView
+ .setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED);
+
+ recipientView.setEmptyTableWidget(new Label("No recipients."));
+
+ // Column<Recipient, String> typeColumn = new Column<Recipient, String>(
+ // new ImageCell())
+ // {
+ // @Override
+ // public String getValue(Recipient object)
+ // {
+ // if (object.getClass().equals(EmailRecipient.class))
+ // {
+ // return "mail.png";
+ // }
+ // else
+ // {
+ // // TODO: test "unknown" scenario
+ // return "unknown.png";
+ // }
+ // }
+ // };
+
+ Column<Recipient, Recipient> removeColumn = new Column<Recipient, Recipient>(
+ new RemoveCell())
+ {
+ @Override
+ public Recipient getValue(Recipient object)
+ {
+ return object;
+ }
+ };
+
+ TextColumn<Recipient> nameColumn = new TextColumn<Recipient>()
+ {
+ @Override
+ public String getValue(Recipient recipient)
+ {
+ return recipient.getName();
+ }
+ };
+
+ TextColumn<Recipient> addressColumn = new TextColumn<Recipient>()
+ {
+ @Override
+ public String getValue(Recipient recipient)
+ {
+ if (recipient.getClass().equals(EmailRecipient.class))
+ {
+ EmailRecipient concrete = (EmailRecipient) recipient;
+ return concrete.getAddress();
+ }
+ else
+ {
+ return "";
+ }
+ }
+ };
+
+ // recipientView.addColumn(typeColumn);
+ recipientView.addColumn(nameColumn);
+ recipientView.addColumn(addressColumn);
+ recipientView.addColumn(removeColumn);
+
+ recipientView.setWidth("100%", true);
+ // recipientView.setColumnWidth(typeColumn, 48, Unit.PX);
+ recipientView.setColumnWidth(nameColumn, 10, Unit.PCT);
+ recipientView.setColumnWidth(addressColumn, 80, Unit.PCT);
+ recipientView.setColumnWidth(removeColumn, 48, Unit.PX);
+
+ initWidget(uiBinder.createAndBindUi(this));
+ }
+
+ @UiHandler("addButton")
+ void handleClick(ClickEvent e)
+ {
+ EmailRecipient recipient = new EmailRecipient();
+
+ final EmailRecipientDialog testView = new EmailRecipientDialog();
+
+ testView.edit(recipient);
+
+ testView.addCloseHandler(new CloseHandler<PopupPanel>()
+ {
+ @Override
+ public void onClose(CloseEvent<PopupPanel> event)
+ {
+ EmailRecipient result = testView.getEditResult();
+ if (result != null)
+ {
+ // TODO: field validation
+ logger.log(Level.INFO, "Update required.");
+ // send recipient to server
+ recipientService.addRecipient(result,
+ new AsyncCallback<Void>()
+ {
+
+ @Override
+ public void onSuccess(Void result)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onFailure(Throwable caught)
+ {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ }
+ }
+ });
+ }
+}
View
12 src/net/brainvitamins/timeout/client/views/RecipientListView.ui.xml
@@ -0,0 +1,12 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+ xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:c="urn:import:com.google.gwt.user.cellview.client">
+
+ <g:VerticalPanel>
+ <c:CellTable ui:field="recipientView" width="100%" />
+ <g:Cell horizontalAlignment="ALIGN_RIGHT">
+ <g:Button ui:field="addButton" text="Add" />
+ </g:Cell>
+ </g:VerticalPanel>
+
+</ui:UiBinder>
View
113 src/net/brainvitamins/timeout/client/views/RemoveCell.java
@@ -0,0 +1,113 @@
+package net.brainvitamins.timeout.client.views;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.brainvitamins.timeout.client.services.RecipientService;
+import net.brainvitamins.timeout.client.services.RecipientServiceAsync;
+import net.brainvitamins.timeout.shared.Recipient;
+
+import com.google.gwt.cell.client.AbstractCell;
+import com.google.gwt.cell.client.Cell;
+import com.google.gwt.cell.client.ValueUpdater;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.EventTarget;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.safehtml.client.SafeHtmlTemplates;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+import com.google.gwt.safehtml.shared.SafeUri;
+import com.google.gwt.safehtml.shared.UriUtils;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * A custom {@link Cell} to show
+ */
+public class RemoveCell extends AbstractCell<Recipient>
+{
+ /**
+ * The HTML templates used to render the cell.
+ */
+ interface Templates extends SafeHtmlTemplates
+ {
+ /**
+ * The template for this Cell, which includes styles and a value.
+ *
+ * @param styles
+ * the styles to include in the style attribute of the div
+ * @param value
+ * the safe value. Since the value type is {@link SafeHtml},
+ * it will not be escaped before including it in the
+ * template. Alternatively, you could make the value type
+ * String, in which case the value would be escaped.
+ * @return a {@link SafeHtml} instance
+ */
+ @SafeHtmlTemplates.Template("<img src=\"remove.png\" />")
+ SafeHtml cell();
+ }
+
+ /**
+ * Create a singleton instance of the templates used to render the cell.
+ */
+ private static Templates templates = GWT.create(Templates.class);
+
+ private static Logger logger = Logger.getLogger("RemoveCell");
+
+ private static RecipientServiceAsync recipientService = GWT
+ .create(RecipientService.class);
+
+ @Override
+ public void render(com.google.gwt.cell.client.Cell.Context context,
+ Recipient value, SafeHtmlBuilder sb)
+ {
+ if (value == null) return;
+
+ SafeHtml rendered;
+ rendered = templates.cell();
+ sb.append(rendered);
+ }
+
+ public RemoveCell()
+ {
+ super("click");
+ }
+
+ @Override
+ public void onBrowserEvent(Context context, Element parent,
+ Recipient value, NativeEvent event,
+ ValueUpdater<Recipient> valueUpdater)
+ {
+ // Handle the click event.
+ if ("click".equals(event.getType()))
+ {
+ // Ignore clicks that occur outside of the outermost element.
+ EventTarget eventTarget = event.getEventTarget();
+ if (parent.getFirstChildElement().isOrHasChild(
+ Element.as(eventTarget)))
+ {
+ logger.log(Level.INFO, "RemoveCell clicked.");
+ recipientService.removeRecipient(value,
+ new AsyncCallback<Boolean>()
+ {
+
+ @Override
+ public void onSuccess(Boolean result)
+ {
+ if (result)
+ logger.log(Level.INFO, "Recipient removed.");
+ else
+ logger.log(Level.INFO, "Recipient removal failed.");
+ }
+
+ @Override
+ public void onFailure(Throwable caught)
+ {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ }
+ }
+ }
+}
View
20 src/net/brainvitamins/timeout/server/ActivityLogger.java
@@ -1,13 +1,11 @@
package net.brainvitamins.timeout.server;
-import java.security.InvalidParameterException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import javax.jdo.PersistenceManager;
-import javax.jdo.Query;
import net.brainvitamins.timeout.shared.Activity;
import net.brainvitamins.timeout.shared.Checkin;
@@ -72,25 +70,14 @@ public void logActivity(String userId, Activity activity)
String time = dateFormat.format(timestamp);
PersistenceManager pm = PMF.get().getPersistenceManager();
- Query query = pm.newQuery(User.class);
- query.setFilter("id == userIdParam");
- query.declareParameters("String userIdParam");
List<Activity> activityLog;
try
{
- Object rawResults = query.execute(userId);
+ User currentUser = pm.getObjectById(User.class, userId);
- List<User> results = (List<User>) rawResults;
-
- // sanity checks
- if (results.isEmpty() || results.size() > 1)
- {
- throw new InvalidParameterException("Invalid user specified");
- }
-
- activityLog = results.get(0).getActivityLog();
+ activityLog = currentUser.getActivityLog();
if (activity.getClass().equals(Checkin.class))
{
@@ -102,7 +89,7 @@ public void logActivity(String userId, Activity activity)
// TODO: verify the task was actually deleted.
- // TODO: move this to a RemoteService so we don't have to
+ // TODO: figure out a way to avoid hardcoding the module path
// hardcode the module path
TaskOptions taskOptions = TaskOptions.Builder
.withUrl("/timeout/timeout").countdownMillis(timeout)
@@ -121,7 +108,6 @@ public void logActivity(String userId, Activity activity)
}
finally
{
- query.closeAll();
pm.close();
}
}
View
19 src/net/brainvitamins/timeout/server/ActivityServiceImpl.java
@@ -1,20 +1,15 @@
package net.brainvitamins.timeout.server;
-import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import javax.jdo.PersistenceManager;
-import javax.jdo.Query;
-
import net.brainvitamins.timeout.client.services.ActivityService;
import net.brainvitamins.timeout.shared.Activity;
import net.brainvitamins.timeout.shared.Checkin;
import net.brainvitamins.timeout.shared.User;
-import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
public class ActivityServiceImpl extends RemoteServiceServlet implements
@@ -29,9 +24,21 @@
@Override
public List<Activity> getActivityLog(int sizeLimit)
{
- User currentUser = DataOperations.GetCurrentUser();
+ User currentUser = DataOperations.getCurrentUserWithActivity();
List<Activity> activityLog = new ArrayList<Activity>();
+
+ if (currentUser == null)
+ {
+ System.out.println("Invalid user!");
+ return activityLog;
+ }
+
+ if (currentUser.getActivityLog() == null)
+ {
+ System.out.println("No activity found for " + currentUser.getNickname());
+ return activityLog;
+ }
Collections.sort(currentUser.getActivityLog(),
new Comparator<Activity>()
View
1 src/net/brainvitamins/timeout/server/CheckinServiceImpl.java
@@ -25,6 +25,7 @@ public void Checkin(long timeout)
throw new IllegalArgumentException(
"Parameter timeout cannot be less than one (was it defined?)");
+ System.out.println("Checkin");
Constants.ACTIVITYSERVICE.logActivity(new Checkin(timestamp, timeout));
}
}
View
85 src/net/brainvitamins/timeout/server/DataOperations.java
@@ -9,39 +9,78 @@
public class DataOperations
{
- public static User GetCurrentUser()
+ /*
+ * Returns a detached copy of the current User data object. The activity log and recipient list are not included.
+ */
+ public static User getCurrentUser()
{
- com.google.appengine.api.users.User user = UserServiceFactory
- .getUserService().getCurrentUser();
-
- // TODO: refactor the JDO query that gets the user into a single method
PersistenceManager pm = PMF.get().getPersistenceManager();
- // Query query = pm.newQuery(User.class);
- // query.setFilter("id == userIdParam");
- // query.declareParameters("String userIdParam");
- //
- // Object rawResults = query.execute(user.getUserId());
- // List<User> results = (List<User>) rawResults;
- //
- // // sanity checks
- // if (results.isEmpty() || results.size() > 1)
- // {
- // throw new InvalidParameterException("Invalid user specified");
- // }
- //
- // User currentUser = results.get(0);
+ try
+ {
+ User currentUser = pm.getObjectById(User.class, getGWTUser().getUserId());
+ User detached = pm.detachCopy(currentUser);
+ return detached;
+ }
+ catch (JDOObjectNotFoundException e)
+ {
+ return null;
+ }
+ finally
+ {
+ pm.close();
+ }
+ }
+ /*
+ * Returns a detached copy of the current user data, with the activity log.
+ */
+ public static User getCurrentUserWithActivity()
+ {
+ PersistenceManager pm = PMF.get().getPersistenceManager();
try
{
- User currentUser = pm.getObjectById(User.class, user.getUserId());
-// if (currentUser == null)
-// throw new InvalidParameterException("Invalid user specified");
+ User currentUser = pm.getObjectById(User.class, getGWTUser().getUserId());
+ currentUser.getActivityLog();
+
+ User detached = pm.detachCopy(currentUser);
+ return detached;
+ }
+ catch (JDOObjectNotFoundException e)
+ {
+ return null;
+ }
+ finally
+ {
+ pm.close();
+ }
+ }
- return currentUser;
+ /*
+ * Returns a detached copy of the current user data, with the activity log.
+ */
+ public static User getCurrentUserWithRecipients()
+ {
+ PersistenceManager pm = PMF.get().getPersistenceManager();
+ try
+ {
+ User currentUser = pm.getObjectById(User.class, getGWTUser().getUserId());
+ currentUser.getRecipients();
+
+ User detached = pm.detachCopy(currentUser);
+ return detached;
}
catch (JDOObjectNotFoundException e)
{
return null;
}
+ finally
+ {
+ pm.close();
+ }
+ }
+
+ private static com.google.appengine.api.users.User getGWTUser()
+ {
+ return UserServiceFactory.getUserService().getCurrentUser();
}
}
View
14 src/net/brainvitamins/timeout/server/LoginServiceImpl.java
@@ -1,9 +1,6 @@
package net.brainvitamins.timeout.server;
-import java.util.List;
-
import javax.jdo.PersistenceManager;
-import javax.jdo.Query;
import net.brainvitamins.timeout.client.services.LoginService;
import net.brainvitamins.timeout.shared.LoginInfo;
@@ -30,15 +27,7 @@ public LoginInfo login(String requestUri)
if (user != null)
{
- // make sure the user exists in the db
-
- // Query query = pm.newQuery(User.class);
- // query.setFilter("id == userIdParam");
- // query.declareParameters("String userIdParam");
-
- // Object rawResults = query.execute(user.getUserId());
- // List<User> results = (List<User>) rawResults;
- User userDataObject = DataOperations.GetCurrentUser();
+ User userDataObject = DataOperations.getCurrentUser();
if (userDataObject == null)
{
PersistenceManager pm = PMF.get().getPersistenceManager();
@@ -49,7 +38,6 @@ public LoginInfo login(String requestUri)
}
finally
{
- // query.closeAll();
pm.close();
}
}
View
98 src/net/brainvitamins/timeout/server/RecipientServiceImpl.java
@@ -1,21 +1,16 @@
package net.brainvitamins.timeout.server;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import javax.jdo.PersistenceManager;
+
import net.brainvitamins.timeout.client.services.RecipientService;
import net.brainvitamins.timeout.shared.Recipient;
+import net.brainvitamins.timeout.shared.User;
-import com.google.appengine.api.datastore.DatastoreService;
-import com.google.appengine.api.datastore.DatastoreServiceFactory;
-import com.google.appengine.api.datastore.Entity;
-import com.google.appengine.api.datastore.FetchOptions;
-import com.google.appengine.api.datastore.Key;
-import com.google.appengine.api.datastore.KeyFactory;
-import com.google.appengine.api.datastore.Query;
-import com.google.appengine.api.taskqueue.QueueFactory;
-import com.google.appengine.api.users.User;
-import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
@@ -27,37 +22,78 @@
*
*/
private static final long serialVersionUID = 6581374344337957491L;
-
+
public static final String recipientKindIdentifier = "Recipient";
@Override
- public void addRecipient()
+ public void addRecipient(Recipient recipient)
{
- // TODO Auto-generated method stub
+ com.google.appengine.api.users.User user = UserServiceFactory
+ .getUserService().getCurrentUser();
+ PersistenceManager pm = PMF.get().getPersistenceManager();
+
+ try
+ {
+ User currentUser = pm.getObjectById(User.class, user.getUserId());
+ currentUser.getRecipients().add(recipient);
+ }
+ finally
+ {
+ pm.close();
+ }
}
@Override
public List<Recipient> getRecipients()
{
- DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
-
- UserService userService = UserServiceFactory.getUserService();
- User user = userService.getCurrentUser();
-
- String userId = user.getUserId();
-
- Key recipientStoreKey = KeyFactory.createKey(recipientKindIdentifier, userId);
-
- Query query = new Query(recipientKindIdentifier, recipientStoreKey);
-
- List<Entity> activityEntries = datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
-
- List<Recipient> recipients = new ArrayList<Recipient>();
-
-
-
- return recipients;
+ List<Recipient> result = new ArrayList<Recipient>(DataOperations
+ .getCurrentUserWithRecipients().getRecipients());
+
+ // sort by name, descending
+ Collections.sort(result, new Comparator<Recipient>()
+ {
+ @Override
+ public int compare(Recipient o1, Recipient o2)
+ {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+
+ return result;
}
+ @Override
+ public boolean removeRecipient(Recipient recipient)
+ {
+ com.google.appengine.api.users.User user = UserServiceFactory
+ .getUserService().getCurrentUser();
+
+ PersistenceManager pm = PMF.get().getPersistenceManager();
+ pm.getFetchPlan().addGroup("withRecipients");
+
+ try
+ {
+ User currentUser = pm.getObjectById(User.class, user.getUserId());
+
+ Recipient ref = null;
+ boolean result = false;
+
+ for (Recipient r : currentUser.getRecipients())
+ {
+ if (r.equals(recipient)) ref = r;
+ }
+
+ if (ref != null)
+ {
+ result = currentUser.getRecipients().remove(ref);
+ }
+
+ return result;
+ }
+ finally
+ {
+ pm.close();
+ }
+ }
}
View
2 src/net/brainvitamins/timeout/server/TimeoutServlet.java
@@ -47,6 +47,8 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp)
Constants.ACTIVITYSERVICE.logActivity(userId, new Timeout(
new Date(), timeout, startTime));
+
+ System.out.println("Timeout logged.");
}
catch (ParseException e)
{
View
55 src/net/brainvitamins/timeout/shared/EmailRecipient.java
@@ -1,25 +1,64 @@
package net.brainvitamins.timeout.shared;
-import javax.jdo.annotations.Extension;
-import javax.jdo.annotations.IdGeneratorStrategy;
+import java.io.Serializable;
+
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
-import javax.jdo.annotations.PrimaryKey;
-import javax.mail.internet.InternetAddress;
@PersistenceCapable
-public class EmailRecipient extends Recipient
+public class EmailRecipient extends Recipient implements Serializable
{
+ /**
+ *
+ */
+ private static final long serialVersionUID = -2341504040837658628L;
+
@Persistent
- private InternetAddress address;
+ private String address;
- public InternetAddress getAddress()
+ public String getAddress()
{
return address;
}
- public EmailRecipient(InternetAddress address)
+ public EmailRecipient()
+ {
+ this("", false, "");
+ }
+
+ public EmailRecipient(String name, boolean verified)
{
+ this(name, verified, "");
+ }
+
+ public EmailRecipient(String name, boolean verified, String address)
+ {
+ super(name, verified);
this.address = address;
}
+
+ @Override
+ public boolean equals(Object other)
+ {
+ if (other instanceof EmailRecipient)
+ {
+ EmailRecipient that = (EmailRecipient) other;
+ return this.getName().equals(that.getName())
+ && this.address.equals(that.getAddress());
+ }
+ return false;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getName() + ":" + getAddress()
+ + (isVerified() ? "(Verified)" : "(Unverified)");
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return getName().hashCode() ^ getAddress().hashCode();
+ }
}
View
43 src/net/brainvitamins/timeout/shared/Recipient.java
@@ -1,5 +1,7 @@
package net.brainvitamins.timeout.shared;
+import java.io.Serializable;
+
import javax.jdo.annotations.Discriminator;
import javax.jdo.annotations.DiscriminatorStrategy;
import javax.jdo.annotations.Extension;
@@ -9,13 +11,19 @@
import javax.jdo.annotations.PrimaryKey;
@PersistenceCapable
-//@Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
-@Discriminator(strategy=DiscriminatorStrategy.CLASS_NAME)
-public abstract class Recipient
+// @Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
+@Discriminator(strategy = DiscriminatorStrategy.CLASS_NAME)
+public abstract class Recipient implements Serializable
{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8775141775408581192L;
+
+ @SuppressWarnings("unused")
@PrimaryKey
- @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
- @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
+ @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
+ @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String key;
// TODO: think of how this could be re-implemented as an enumeration
@@ -27,8 +35,31 @@ public boolean isVerified()
return verified;
}
- public void setVerified(boolean verified)
+ @Persistent
+ private String name;
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ public Recipient()
+ {
+ this("", false);
+ }
+
+ public Recipient(String name)
+ {
+ this(name, false);
+ }
+
+ public Recipient(String name, boolean verified)
{
+ super();
this.verified = verified;
+ this.name = name;
}
}
View
10 src/net/brainvitamins/timeout/shared/User.java
@@ -5,22 +5,24 @@
import java.util.List;
import java.util.Set;
+import javax.jdo.annotations.FetchGroup;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
-@PersistenceCapable
+@PersistenceCapable(detachable = "true")
+@FetchGroup(name = "withRecipients", members = { @Persistent(name = "recipients") })
public class User
{
@PrimaryKey
private String id;
private String nickname;
- @Persistent
+ @Persistent(dependentElement = "true")
private List<Activity> activityLog;
- @Persistent
+ @Persistent(dependentElement = "true")
private Set<Recipient> recipients;
/**
@@ -49,6 +51,8 @@ public String getNickname()
*/
public Set<Recipient> getRecipients()
{
+ // this data model does not share recipients between users.
+ // this may prove inefficient at scale.
return recipients;
}
View
1 war/.gitignore
@@ -1 +1,2 @@
/hellohello
+/timeout
View
10 war/WEB-INF/web.xml
@@ -55,6 +55,16 @@
<url-pattern>/timeout/activity</url-pattern>
</servlet-mapping>
+ <servlet>
+ <servlet-name>recipientService</servlet-name>
+ <servlet-class>net.brainvitamins.timeout.server.RecipientServiceImpl</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>recipientService</servlet-name>
+ <url-pattern>/timeout/recipient</url-pattern>
+ </servlet-mapping>
+
<!-- Default page to serve -->
<welcome-file-list>
<!-- <welcome-file>landing.jsp</welcome-file> -->
View
6 war/stylesheets/main.css
@@ -17,6 +17,7 @@ div#user {
text-align: right;
}
+/* TODO: apply these to celltables: https://developers.google.com/web-toolkit/doc/latest/DevGuideUiCustomCells */
div.checkin {
margin: 1px;
background-color: lightblue;
@@ -29,4 +30,9 @@ div.timeout {
p.salute {
text-align: right;
+}
+
+span.inputLabel {
+ width: 300px;
+ background-color: lightblue;
}

0 comments on commit 1712345

Please sign in to comment.
Something went wrong with that request. Please try again.