[kune-commits] r1648 - in trunk: . src/main/java/cc/kune/common/client/actions/ui/descrip src/main/java/cc/kune/core/server src/main/java/cc/kune/core/server/mail src/main/java/cc/kune/core/server/notifier src/main/java/cc/kune/core/server/properties src/main/java/cc/kune/core/server/rack src/main/java/cc/kune/core/server/rpc src/main/java/cc/kune/core/server/scheduler src/main/java/cc/kune/core/server/testhelper src/main/java/cc/kune/core/shared/domain src/main/java/cc/kune/core/shared/domain/dto src/main/java/cc/kune/core/shared/dto src/main/java/cc/kune/domain src/main/java/cc/kune/events/client/actions src/main/java/cc/kune/events/client/viewer src/main/java/cc/kune/wave/server src/main/resources src/test/java/cc/kune/core/server src/test/java/cc/kune/core/server/mail

Vicente J. Ruiz Jurado vjrj_ at ourproject.org
Tue Dec 27 02:32:09 CET 2011


Author: vjrj_
Date: 2011-12-27 02:32:05 +0100 (Tue, 27 Dec 2011)
New Revision: 1648

Added:
   trunk/src/main/java/cc/kune/core/server/notifier/
   trunk/src/main/java/cc/kune/core/server/notifier/NotifyService.java
   trunk/src/main/java/cc/kune/core/server/notifier/NotifyServiceDefault.java
   trunk/src/main/java/cc/kune/core/server/notifier/NotifyType.java
   trunk/src/main/java/cc/kune/core/server/notifier/UserOnlineSimpleManager.java
   trunk/src/main/java/cc/kune/core/server/notifier/UsersOnline.java
   trunk/src/main/java/cc/kune/core/server/scheduler/
   trunk/src/main/java/cc/kune/core/server/scheduler/AbstractJob.java
   trunk/src/main/java/cc/kune/core/server/scheduler/CronServerTasksManager.java
   trunk/src/main/java/cc/kune/core/server/scheduler/DailyJob.java
   trunk/src/main/java/cc/kune/core/server/scheduler/HourlyJob.java
   trunk/src/main/java/cc/kune/core/server/testhelper/
   trunk/src/main/java/cc/kune/core/server/testhelper/ctx/
   trunk/src/main/java/cc/kune/core/shared/domain/dto/
   trunk/src/main/java/cc/kune/core/shared/domain/dto/EmailNotificationFrequency.java
   trunk/src/main/java/cc/kune/wave/server/WaveEmailNotifier.java
   trunk/src/test/java/cc/kune/core/server/mail/
   trunk/src/test/java/cc/kune/core/server/mail/MailServiceDefaultTest.java
Removed:
   trunk/src/test/java/cc/kune/core/server/testhelper/
Modified:
   trunk/.classpath
   trunk/TODO
   trunk/pom.xml
   trunk/src/main/java/cc/kune/common/client/actions/ui/descrip/PushButtonDescriptor.java
   trunk/src/main/java/cc/kune/core/server/KuneRackModule.java
   trunk/src/main/java/cc/kune/core/server/PlatformServerModule.java
   trunk/src/main/java/cc/kune/core/server/UserSessionManager.java
   trunk/src/main/java/cc/kune/core/server/mail/MailService.java
   trunk/src/main/java/cc/kune/core/server/mail/MailServiceDefault.java
   trunk/src/main/java/cc/kune/core/server/properties/KuneBasicProperties.java
   trunk/src/main/java/cc/kune/core/server/rack/RackServletFilter.java
   trunk/src/main/java/cc/kune/core/server/rpc/UserRPC.java
   trunk/src/main/java/cc/kune/core/shared/dto/UserDTO.java
   trunk/src/main/java/cc/kune/domain/User.java
   trunk/src/main/java/cc/kune/events/client/actions/EventAddMenuItem.java
   trunk/src/main/java/cc/kune/events/client/viewer/CalendarViewerPresenter.java
   trunk/src/main/java/cc/kune/wave/server/KuneAgent.java
   trunk/src/main/java/cc/kune/wave/server/KuneWaveService.java
   trunk/src/main/java/cc/kune/wave/server/KuneWaveServiceDefault.java
   trunk/src/main/java/cc/kune/wave/server/ParticipantUtils.java
   trunk/src/main/resources/kune.properties
Log:
NEW - # 164: NotificationManager in server side 
http://kune.ourproject.org/issues/ticket/164

Modified: trunk/.classpath
===================================================================
--- trunk/.classpath	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/.classpath	2011-12-27 01:32:05 UTC (rev 1648)
@@ -124,6 +124,7 @@
   <classpathentry kind="var" path="M2_REPO/org/codehaus/plexus/plexus-container-default/1.0-alpha-8/plexus-container-default-1.0-alpha-8.jar" sourcepath="M2_REPO/org/codehaus/plexus/plexus-container-default/1.0-alpha-8/plexus-container-default-1.0-alpha-8-sources.jar"/>
   <classpathentry kind="var" path="M2_REPO/org/codehaus/plexus/plexus-utils/1.0.4/plexus-utils-1.0.4.jar" sourcepath="M2_REPO/org/codehaus/plexus/plexus-utils/1.0.4/plexus-utils-1.0.4-sources.jar"/>
   <classpathentry kind="var" path="M2_REPO/org/waveprotocol/proto-msg/0.3.8/proto-msg-0.3.8.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/quartz-scheduler/quartz/2.1.0/quartz-2.1.0.jar" sourcepath="M2_REPO/org/quartz-scheduler/quartz/2.1.0/quartz-2.1.0-sources.jar"/>
   <classpathentry kind="var" path="M2_REPO/org/w3c/css/sac/1.3/sac-1.3.jar" sourcepath="M2_REPO/org/w3c/css/sac/1.3/sac-1.3-sources.jar"/>
   <classpathentry kind="var" path="M2_REPO/org/seleniumhq/selenium/selenium-android-driver/2.12.0/selenium-android-driver-2.12.0.jar" sourcepath="M2_REPO/org/seleniumhq/selenium/selenium-android-driver/2.12.0/selenium-android-driver-2.12.0-sources.jar"/>
   <classpathentry kind="var" path="M2_REPO/org/seleniumhq/selenium/selenium-api/2.12.0/selenium-api-2.12.0.jar" sourcepath="M2_REPO/org/seleniumhq/selenium/selenium-api/2.12.0/selenium-api-2.12.0-sources.jar"/>

Modified: trunk/TODO
===================================================================
--- trunk/TODO	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/TODO	2011-12-27 01:32:05 UTC (rev 1648)
@@ -12,6 +12,13 @@
    nick == assigned to
 
 * SHORT-TERM (URGENT)
+** Emil notifications for new waves
+Email Notifications
+How often do you want to receive notifications?
+    almost immediately
+    at most hourly
+    at most daily
+    I don't need email notifications
 ** group.inexistentTool nasty error
 ** calendar support
 *** gwt-cal + http://ical4j.sourceforge.net/introduction.html

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/pom.xml	2011-12-27 01:32:05 UTC (rev 1648)
@@ -694,6 +694,11 @@
       <artifactId>mail</artifactId>
       <version>1.4.4</version>
     </dependency>
+    <dependency>
+      <groupId>org.quartz-scheduler</groupId>
+      <artifactId>quartz</artifactId>
+      <version>2.1.0</version>
+    </dependency>
     <!-- calendar -->
     <dependency>
       <groupId>com.bradrydzewski</groupId>

Modified: trunk/src/main/java/cc/kune/common/client/actions/ui/descrip/PushButtonDescriptor.java
===================================================================
--- trunk/src/main/java/cc/kune/common/client/actions/ui/descrip/PushButtonDescriptor.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/common/client/actions/ui/descrip/PushButtonDescriptor.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -20,34 +20,40 @@
 package cc.kune.common.client.actions.ui.descrip;
 
 import cc.kune.common.client.actions.AbstractAction;
+import cc.kune.common.client.actions.Action;
 
 public class PushButtonDescriptor extends ButtonDescriptor {
 
-    public static final String PUSHED = "pushed";
+  public static final String PUSHED = "pushed";
 
-    public PushButtonDescriptor(final AbstractAction action) {
-        super(action);
-        setPushedImpl(false);
-    }
+  public PushButtonDescriptor(final AbstractAction action) {
+    super(action);
+    setPushedImpl(false);
+  }
 
-    public PushButtonDescriptor(final PushButtonDescriptor button) {
-        this(button.getAction());
-    }
+  public PushButtonDescriptor(final PushButtonDescriptor button) {
+    this(button.getAction());
+  }
 
-    @Override
-    public Class<?> getType() {
-        return PushButtonDescriptor.class;
-    }
+  public PushButtonDescriptor(final String text, final AbstractAction action) {
+    this(action);
+    putValue(Action.NAME, text);
+  }
 
-    public boolean isPushed() {
-        return (Boolean) getValue(PUSHED);
-    }
+  @Override
+  public Class<?> getType() {
+    return PushButtonDescriptor.class;
+  }
 
-    public void setPushed(final boolean pushed) {
-        setPushedImpl(pushed);
-    }
+  public boolean isPushed() {
+    return (Boolean) getValue(PUSHED);
+  }
 
-    private void setPushedImpl(final boolean pushed) {
-        putValue(PUSHED, pushed);
-    }
+  public void setPushed(final boolean pushed) {
+    setPushedImpl(pushed);
+  }
+
+  private void setPushedImpl(final boolean pushed) {
+    putValue(PUSHED, pushed);
+  }
 }

Modified: trunk/src/main/java/cc/kune/core/server/KuneRackModule.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/KuneRackModule.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/server/KuneRackModule.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -115,7 +115,6 @@
         install(FinderRegistry.init(jpaPersistModule));
         bindInterceptor(Matchers.annotatedWith(LogThis.class), new NotInObject(),
             new LoggerMethodInterceptor());
-        // bindConstant().annotatedWith(PropertiesFileName.class).to(kuneConfig);
         if (sessionScope != null) {
           bindScope(SessionScoped.class, sessionScope);
         }

Modified: trunk/src/main/java/cc/kune/core/server/PlatformServerModule.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/PlatformServerModule.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/server/PlatformServerModule.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -86,6 +86,9 @@
 import cc.kune.core.server.manager.impl.UserManagerDefault;
 import cc.kune.core.server.mapper.DozerMapper;
 import cc.kune.core.server.mapper.Mapper;
+import cc.kune.core.server.notifier.NotifyService;
+import cc.kune.core.server.notifier.NotifyServiceDefault;
+import cc.kune.core.server.notifier.UsersOnline;
 import cc.kune.core.server.rpc.ContentRPC;
 import cc.kune.core.server.rpc.GroupRPC;
 import cc.kune.core.server.rpc.I18nRPC;
@@ -93,6 +96,7 @@
 import cc.kune.core.server.rpc.SocialNetworkRPC;
 import cc.kune.core.server.rpc.StatsRPC;
 import cc.kune.core.server.rpc.UserRPC;
+import cc.kune.core.server.scheduler.CronServerTasksManager;
 import cc.kune.core.server.state.StateService;
 import cc.kune.core.server.state.StateServiceDefault;
 import cc.kune.core.server.stats.StatsService;
@@ -109,7 +113,9 @@
 import cc.kune.wave.server.KuneWaveService;
 import cc.kune.wave.server.KuneWaveServiceDefault;
 import cc.kune.wave.server.ParticipantUtils;
+import cc.kune.wave.server.WaveEmailNotifier;
 
+import com.google.inject.Singleton;
 import com.google.inject.matcher.Matchers;
 
 public class PlatformServerModule extends AbstractExtendedModule {
@@ -186,7 +192,11 @@
     bind(EntityLogoUploadManager.class);
     bind(EntityLogoDownloadManager.class);
     bind(ParticipantUtils.class);
+    bind(UsersOnline.class).to(UserSessionManager.class).in(Singleton.class);
     requestStaticInjection(AccessRightsUtils.class);
+    bind(CronServerTasksManager.class).in(Singleton.class);
+    bind(NotifyService.class).to(NotifyServiceDefault.class).in(Singleton.class);
+    bind(WaveEmailNotifier.class).in(Singleton.class);
     bindInterceptor(Matchers.any(), Matchers.annotatedWith(Authenticated.class),
         outermostCall(new AuthenticatedMethodInterceptor()));
     bindInterceptor(Matchers.any(), Matchers.annotatedWith(Authorizated.class),

Modified: trunk/src/main/java/cc/kune/core/server/UserSessionManager.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/UserSessionManager.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/server/UserSessionManager.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -19,7 +19,11 @@
  */
 package cc.kune.core.server;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import cc.kune.core.server.manager.UserManager;
+import cc.kune.core.server.notifier.UsersOnline;
 import cc.kune.domain.User;
 
 import com.google.inject.Inject;
@@ -27,8 +31,9 @@
 import com.google.inject.Singleton;
 
 @Singleton
-public class UserSessionManager {
+public class UserSessionManager implements UsersOnline {
 
+  private final Set<String> logins;
   private final UserManager manager;
   private final Provider<UserSession> userSessionProv;
 
@@ -36,12 +41,25 @@
   public UserSessionManager(final UserManager manager, final Provider<UserSession> userSessionProv) {
     this.manager = manager;
     this.userSessionProv = userSessionProv;
+    // For now the implementation of this can be very inaccurate (if we
+    // login/logout several times with different clients) and not scalable
+    // (stored in a MAP). Possible fix, to use jabber status
+    // More info:
+    // http://stackoverflow.com/questions/4592961/how-can-i-check-which-all-users-are-logged-into-my-application
+    //
+    // Best way maybe it's accessing openfire db:
+    // http://www.mastertheboss.com/jboss-howto/45-jboss-persistence/110-jboss-persistencexml-multiple-database.html
+    logins = new HashSet<String>();
   }
 
   public String getHash() {
     return getUserSession().getHash();
   }
 
+  private String getUserLoggedShortName() {
+    return getUser().getShortName();
+  }
+
   public User getUser() {
     return manager.find(getUserSession().getUserId());
   }
@@ -50,6 +68,11 @@
     return userSessionProv.get();
   }
 
+  @Override
+  public boolean isLogged(final String shortname) {
+    return logins.contains(shortname);
+  }
+
   public boolean isUserLoggedIn() {
     return getUserSession().getUserId() != null;
   }
@@ -61,10 +84,20 @@
   public void login(final Long userId, final String newUserHash) {
     getUserSession().setUserId(userId);
     getUserSession().setHash(newUserHash);
+    updateLoggedUser();
   }
 
   public void logout() {
+    if (isUserLoggedIn()) {
+      logins.remove(getUserLoggedShortName());
+    }
     getUserSession().setUserId(null);
     getUserSession().setHash(null);
   }
+
+  public void updateLoggedUser() {
+    if (isUserLoggedIn()) {
+      logins.add(getUserLoggedShortName());
+    }
+  }
 }

Modified: trunk/src/main/java/cc/kune/core/server/mail/MailService.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/mail/MailService.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/server/mail/MailService.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -30,10 +30,10 @@
    *          the email subject
    * @param body
    *          the body of the email in html format
-   * @param to
-   *          the recipient
+   * @param tos
+   *          the recipients
    */
-  void sendHtml(FormatedString subject, FormatedString body, String to);
+  void sendHtml(FormatedString subject, FormatedString body, String... tos);
 
   /**
    * Sends html email
@@ -47,7 +47,7 @@
    * @param tos
    *          the recipients
    */
-  void sendHtml(FormatedString subject, FormatedString body, String from, String... tos);
+  void sendHtml(String from, FormatedString subject, FormatedString body, String... tos);
 
   /**
    * 
@@ -57,10 +57,10 @@
    *          the email subject
    * @param body
    *          the body of the email in text format
-   * @param to
-   *          the recipient
+   * @param tos
+   *          the recipients
    */
-  void sendPlain(FormatedString subject, FormatedString body, String to);
+  void sendPlain(FormatedString subject, FormatedString body, String... tos);
 
   /**
    * Sends plain email
@@ -74,6 +74,6 @@
    * @param tos
    *          the recipients
    */
-  void sendPlain(FormatedString subject, FormatedString body, String from, String... tos);
+  void sendPlain(String from, FormatedString subject, FormatedString body, String... tos);
 
 }

Modified: trunk/src/main/java/cc/kune/core/server/mail/MailServiceDefault.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/mail/MailServiceDefault.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/server/mail/MailServiceDefault.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -32,26 +32,55 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import cc.kune.core.client.errors.DefaultException;
+import cc.kune.core.server.LogThis;
 import cc.kune.core.server.properties.KuneProperties;
 
+import com.google.common.base.Preconditions;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
 @Singleton
+ at LogThis
 public class MailServiceDefault implements MailService {
 
   public static class FormatedString {
+    public static FormatedString build(final String plainMsg) {
+      return new FormatedString(plainMsg);
+    }
 
-    private final String string;
+    public static FormatedString build(final String template, final Object... args) {
+      return new FormatedString(template, args);
+    }
 
+    private final Object[] args;
+    private String template;
+
+    public FormatedString(final String plainMsg) {
+      template = plainMsg;
+      args = null;
+    }
+
     public FormatedString(final String template, final Object... args) {
-      string = String.format(template, args);
+      this.template = template;
+      this.args = args;
     }
 
     public String getString() {
-      return string;
+      Preconditions.checkNotNull(template, "Template of FormatedString cannot be null");
+      return args == null ? template : String.format(template, args);
     }
+
+    public String getTemplate() {
+      return template;
+    }
+
+    /*
+     * Used to translate the template to the user language (when you don't know
+     * already the language of the user)
+     */
+    public void setTemplate(final String template) {
+      this.template = template;
+    }
   }
 
   Log log = LogFactory.getLog(MailServiceDefault.class);
@@ -68,8 +97,8 @@
     props.put("mail.smtp.host", smtpServer);
   }
 
-  private void send(final FormatedString subject, final FormatedString body, final boolean isHtml,
-      final String from, final String... tos) {
+  private void send(final String from, final FormatedString subject, final FormatedString body,
+      final boolean isHtml, final String... tos) {
     if (smtpSkip) {
       return;
     }
@@ -79,47 +108,50 @@
 
     // Define message
     final MimeMessage message = new MimeMessage(session);
-    try {
-      message.setFrom(new InternetAddress(from));
-      for (final String to : tos) {
+    for (final String to : tos) {
+      try {
+        message.setFrom(new InternetAddress(from));
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
+        final String formatedSubject = subject.getString();
+        message.setSubject(formatedSubject);
+        final String formatedBody = body.getString();
+        if (isHtml) {
+          message.setContent(formatedBody, "text/html");
+        } else {
+          message.setText(formatedBody);
+        }
+        // Send message
+        Transport.send(message);
+      } catch (final AddressException e) {
+      } catch (final MessagingException e) {
+        final String error = String.format(
+            "Error sendind an email to %s, with subject: %s, and body: %s", from, subject, to);
+        log.error(error, e);
+        // Better not to throw exceptions because users emails can be wrong...
+        // throw new DefaultException(error, e);
       }
-      final String formatedSubject = subject.getString();
-      message.setSubject(formatedSubject);
-      final String formatedBody = body.getString();
-      if (isHtml) {
-        message.setContent(formatedBody, "text/html");
-      } else {
-        message.setText(formatedBody);
-      }
-      // Send message
-      Transport.send(message);
-    } catch (final AddressException e) {
-    } catch (final MessagingException e) {
-      log.error("Error sending the email", e);
-      throw new DefaultException("Error sending an email");
     }
   }
 
   @Override
-  public void sendHtml(final FormatedString subject, final FormatedString body, final String to) {
-    send(subject, body, true, smtpDefaultFrom, to);
+  public void sendHtml(final FormatedString subject, final FormatedString body, final String... tos) {
+    send(smtpDefaultFrom, subject, body, true, tos);
   }
 
   @Override
-  public void sendHtml(final FormatedString subject, final FormatedString body, final String from,
+  public void sendHtml(final String from, final FormatedString subject, final FormatedString body,
       final String... tos) {
-    send(subject, body, true, from, tos);
+    send(from, subject, body, true, tos);
   }
 
   @Override
-  public void sendPlain(final FormatedString subject, final FormatedString body, final String to) {
-    send(subject, body, false, smtpDefaultFrom, to);
+  public void sendPlain(final FormatedString subject, final FormatedString body, final String... tos) {
+    send(smtpDefaultFrom, subject, body, false, tos);
   }
 
   @Override
-  public void sendPlain(final FormatedString subject, final FormatedString body, final String from,
+  public void sendPlain(final String from, final FormatedString subject, final FormatedString body,
       final String... tos) {
-    send(subject, body, false, from, tos);
+    send(from, subject, body, false, tos);
   }
 }

Added: trunk/src/main/java/cc/kune/core/server/notifier/NotifyService.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/notifier/NotifyService.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/notifier/NotifyService.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,45 @@
+package cc.kune.core.server.notifier;
+
+import cc.kune.core.server.mail.MailServiceDefault.FormatedString;
+
+/**
+ * The Interface NotifyService.
+ */
+public interface NotifyService {
+
+  /**
+   * Send a message to the recipients (also translate the subject/body using the
+   * user language)
+   * 
+   * @param notifyType
+   *          the notify type (email, etc)
+   * @param subject
+   *          the subject of the message (not translated)
+   * @param body
+   *          the body of the message (not translated) but with %s
+   *          {@link String.format} args
+   * @param isHtml
+   *          if the body is html
+   * @param recipients
+   *          the recipients shortnames (without domain)
+   */
+  void send(NotifyType notifyType, FormatedString subject, FormatedString body, boolean isHtml,
+      String... recipients);
+
+  /**
+   * Send a message to the recipients (also translate the subject/body using the
+   * user language)
+   * 
+   * @param notifyType
+   *          the notify type (email, etc)
+   * @param subject
+   *          the subject of the message
+   * @param body
+   *          the body of the message (no translated) but with some %s
+   *          {@link String.format} args
+   * @param recipients
+   *          the recipients shortnames (without domain)
+   */
+  void send(NotifyType notifyType, FormatedString subject, FormatedString body, String... recipients);
+
+}

Added: trunk/src/main/java/cc/kune/core/server/notifier/NotifyServiceDefault.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/notifier/NotifyServiceDefault.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/notifier/NotifyServiceDefault.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,89 @@
+package cc.kune.core.server.notifier;
+
+import javax.persistence.NoResultException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import cc.kune.core.server.mail.MailService;
+import cc.kune.core.server.mail.MailServiceDefault.FormatedString;
+import cc.kune.core.server.manager.I18nTranslationManager;
+import cc.kune.core.server.manager.UserManager;
+import cc.kune.core.server.xmpp.XmppManager;
+import cc.kune.domain.User;
+import cc.kune.wave.server.KuneWaveService;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+ at Singleton
+public class NotifyServiceDefault implements NotifyService {
+  public static final Log LOG = LogFactory.getLog(NotifyServiceDefault.class);
+  private final I18nTranslationManager i18n;
+  private final MailService mailService;
+  private final UserManager userManager;
+  private final KuneWaveService waveService;
+  private final XmppManager xmppManager;
+
+  @Inject
+  public NotifyServiceDefault(final MailService mailService, final KuneWaveService waveService,
+      final XmppManager xmppManager, final UserManager userManager, final I18nTranslationManager i18n) {
+    this.mailService = mailService;
+    this.waveService = waveService;
+    this.xmppManager = xmppManager;
+    this.userManager = userManager;
+    this.i18n = i18n;
+  }
+
+  @Override
+  public void send(final NotifyType notifyType, final FormatedString subject, final FormatedString body,
+      final boolean isHtml, final String... recipients) {
+    for (final String recipient : recipients) {
+      User user;
+      try {
+        user = userManager.findByShortname(recipient);
+      } catch (final NoResultException e) {
+        LOG.info(String.format("The recipient %s is not a local user, don't notify", recipient));
+        continue;
+      }
+      // Translate per recipient language
+      final String subjectTranslation = i18n.getTranslation(user.getLanguage().getCode(),
+          subject.getTemplate(), "");
+      if (subjectTranslation != null) {
+        subject.setTemplate(subjectTranslation);
+      }
+      final String bodyTranslation = i18n.getTranslation(user.getLanguage().getCode(),
+          body.getTemplate(), "");
+      if (bodyTranslation != null) {
+        body.setTemplate(bodyTranslation);
+      }
+      switch (notifyType) {
+      case chat:
+        xmppManager.sendMessage(recipient,
+            String.format("<b>%s</b>%s", subject.getString(), body.getString()));
+        break;
+      case email:
+        final String email = user.getEmail();
+        if (isHtml) {
+          mailService.sendHtml(subject, body, email);
+        } else {
+          mailService.sendPlain(subject, body, email);
+        }
+        break;
+      case wave:
+        if (isHtml) {
+          LOG.error("Wave html messages not supported yet");
+        }
+        waveService.createWave(subject.getString(), body.getString(), recipient);
+        break;
+      }
+    }
+  }
+
+  @Override
+  public void send(final NotifyType notifyType, final FormatedString subject, final FormatedString body,
+      final String... dests) {
+    send(notifyType, subject, body, false, dests);
+  }
+
+}

Added: trunk/src/main/java/cc/kune/core/server/notifier/NotifyType.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/notifier/NotifyType.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/notifier/NotifyType.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,9 @@
+package cc.kune.core.server.notifier;
+
+/**
+ * Type of notifications for users
+ * 
+ */
+public enum NotifyType {
+  chat, email, wave
+}

Added: trunk/src/main/java/cc/kune/core/server/notifier/UserOnlineSimpleManager.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/notifier/UserOnlineSimpleManager.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/notifier/UserOnlineSimpleManager.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,5 @@
+package cc.kune.core.server.notifier;
+
+public class UserOnlineSimpleManager {
+
+}

Added: trunk/src/main/java/cc/kune/core/server/notifier/UsersOnline.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/notifier/UsersOnline.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/notifier/UsersOnline.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,16 @@
+package cc.kune.core.server.notifier;
+
+public interface UsersOnline {
+
+  /**
+   * For now the implementation of this can be very inaccurate (if we
+   * login/logout several times with different clients) and not scalable (stored
+   * in a MAP). Possible fix, to use jabber status
+   * 
+   * @param shortname
+   *          of the user
+   * @return true if is logged
+   */
+  boolean isLogged(String shortname);
+
+}

Modified: trunk/src/main/java/cc/kune/core/server/properties/KuneBasicProperties.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/properties/KuneBasicProperties.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/server/properties/KuneBasicProperties.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -62,10 +62,25 @@
     return properties.get(KuneProperties.SITE_SHORTNAME);
   }
 
+  /**
+   * Gets the site common name (can be something like "this foo site" not
+   * necessary a domain)
+   * 
+   * @return the site common name
+   */
   public String getSiteCommonName() {
     return properties.get(KuneProperties.SITE_COMMON_NAME);
   }
 
+  /**
+   * Gets the site domain (something like example.org)
+   * 
+   * @return the site domain
+   */
+  public String getSiteDomain() {
+    return properties.get(KuneProperties.SITE_DOMAIN);
+  }
+
   public String getWelcomewave() {
     return properties.get(KuneProperties.WELCOME_WAVE);
   }

Modified: trunk/src/main/java/cc/kune/core/server/rack/RackServletFilter.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/rack/RackServletFilter.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/server/rack/RackServletFilter.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -32,11 +32,14 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.quartz.SchedulerException;
 import org.waveprotocol.box.server.rpc.ServerRpcProvider;
 
 import cc.kune.core.server.ServerException;
 import cc.kune.core.server.rack.dock.Dock;
 import cc.kune.core.server.rack.dock.RequestMatcher;
+import cc.kune.core.server.scheduler.CronServerTasksManager;
+import cc.kune.wave.server.WaveEmailNotifier;
 
 import com.google.inject.Injector;
 
@@ -137,14 +140,18 @@
     final RackBuilder builder = new RackBuilder();
     module.configure(builder);
     rack = builder.getRack();
-    // final WaveStarter waveStarter = new WaveStarter();
-    // final Injector waveChildInjector = waveStarter.runMain();
     injector = (Injector) filterConfig.getServletContext().getAttribute(INJECTOR_PARENT_ATTRIBUTE);
     final Injector kuneChildInjector = installInjector(filterConfig, rack, injector);
     startContainerListeners(rack.getListeners(), kuneChildInjector);
     docks = rack.getDocks();
     excludes = rack.getExcludes();
     initFilters(filterConfig);
+    kuneChildInjector.getInstance(WaveEmailNotifier.class);
+    try {
+      kuneChildInjector.getInstance(CronServerTasksManager.class).start();
+    } catch (final SchedulerException e) {
+      LOG.error("Error starting cron scheduler", e);
+    }
     LOG.debug("INITIALIZATION DONE!");
   }
 

Modified: trunk/src/main/java/cc/kune/core/server/rpc/UserRPC.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/rpc/UserRPC.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/server/rpc/UserRPC.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -162,16 +162,14 @@
   @Transactional
   public void logout(final String userHash) throws DefaultException {
     userSessionManager.logout();
-    // FIXME final SessionService sessionService =
-    // sessionServiceProvider.get();
-    // FIXME sessionService.getNewSession();
   }
 
   @Override
   @Authenticated(mandatory = false)
   @Transactional
   public void onlyCheckSession(final String userHash) throws DefaultException {
-    // Do nothing @Authenticated checks user session
+    // Do almost nothing @Authenticated checks user session
+    userSessionManager.updateLoggedUser();
   }
 
   @Override
@@ -179,6 +177,7 @@
   @Transactional
   public UserInfoDTO reloadUserInfo(final String userHash) throws DefaultException {
     final User user = userSessionManager.getUser();
+    userSessionManager.updateLoggedUser();
     return loadUserInfo(user);
   }
 
@@ -205,6 +204,7 @@
       throw new AccessViolationException();
     }
     final User userUpdated = userManager.update(id, user, lang);
+    userSessionManager.updateLoggedUser();
     return contentRPC.getContent(userHash, userUpdated.getUserGroup().getStateToken());
   }
 }

Added: trunk/src/main/java/cc/kune/core/server/scheduler/AbstractJob.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/scheduler/AbstractJob.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/scheduler/AbstractJob.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,26 @@
+package cc.kune.core.server.scheduler;
+
+import static org.quartz.CronScheduleBuilder.cronSchedule;
+import static org.quartz.JobBuilder.newJob;
+import static org.quartz.TriggerBuilder.newTrigger;
+
+import java.text.ParseException;
+
+import org.quartz.CronTrigger;
+import org.quartz.Job;
+import org.quartz.JobDetail;
+import org.quartz.SchedulerException;
+
+public abstract class AbstractJob implements Job {
+  private static final String DEF_GROUP = "groupdef";
+
+  public AbstractJob(final CronServerTasksManager manager, final String cronExpression)
+      throws ParseException, SchedulerException {
+    final JobDetail job = newJob(HourlyJob.class).withIdentity("hourlyjob1", DEF_GROUP).build();
+
+    final CronTrigger trigger = newTrigger().withIdentity("hourlytrigger1", DEF_GROUP).withSchedule(
+        cronSchedule(cronExpression)).build();
+
+    manager.scheduleJob(job, trigger);
+  }
+}

Added: trunk/src/main/java/cc/kune/core/server/scheduler/CronServerTasksManager.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/scheduler/CronServerTasksManager.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/scheduler/CronServerTasksManager.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,34 @@
+package cc.kune.core.server.scheduler;
+
+import java.util.Date;
+
+import org.quartz.CronTrigger;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.impl.StdSchedulerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+ at Singleton
+public class CronServerTasksManager {
+  private final Scheduler sched;
+
+  @Inject
+  public CronServerTasksManager(final StdSchedulerFactory sf) throws SchedulerException {
+    sched = sf.getScheduler();
+  }
+
+  public Date scheduleJob(final JobDetail job, final CronTrigger trigger) throws SchedulerException {
+    return sched.scheduleJob(job, trigger);
+  }
+
+  public void start() throws SchedulerException {
+    sched.start();
+  }
+
+  public void stop() throws SchedulerException {
+    sched.shutdown(true);
+  }
+}

Added: trunk/src/main/java/cc/kune/core/server/scheduler/DailyJob.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/scheduler/DailyJob.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/scheduler/DailyJob.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,13 @@
+package cc.kune.core.server.scheduler;
+
+import java.text.ParseException;
+
+import org.quartz.SchedulerException;
+
+public abstract class DailyJob extends AbstractJob {
+
+  public DailyJob(final CronServerTasksManager manager) throws ParseException, SchedulerException {
+    super(manager, "0 0 * * * ?");
+  }
+
+}

Added: trunk/src/main/java/cc/kune/core/server/scheduler/HourlyJob.java
===================================================================
--- trunk/src/main/java/cc/kune/core/server/scheduler/HourlyJob.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/server/scheduler/HourlyJob.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,13 @@
+package cc.kune.core.server.scheduler;
+
+import java.text.ParseException;
+
+import org.quartz.SchedulerException;
+
+public abstract class HourlyJob extends AbstractJob {
+
+  public HourlyJob(final CronServerTasksManager manager) throws ParseException, SchedulerException {
+    super(manager, "0 * * * * ?");
+  }
+
+}

Copied: trunk/src/main/java/cc/kune/core/server/testhelper/ctx (from rev 1647, trunk/src/test/java/cc/kune/core/server/testhelper/ctx)

Added: trunk/src/main/java/cc/kune/core/shared/domain/dto/EmailNotificationFrequency.java
===================================================================
--- trunk/src/main/java/cc/kune/core/shared/domain/dto/EmailNotificationFrequency.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/core/shared/domain/dto/EmailNotificationFrequency.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (C) 2007-2009 The kune development team (see CREDITS for details)
+ * This file is part of kune.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package cc.kune.core.shared.domain.dto;
+
+import com.google.gwt.user.client.rpc.IsSerializable;
+
+public enum EmailNotificationFrequency implements IsSerializable {
+  daily, // at most daily,
+  hourly, // at most hourly
+  immediately, // almost immediately
+  no // I don't need email notifications
+}

Modified: trunk/src/main/java/cc/kune/core/shared/dto/UserDTO.java
===================================================================
--- trunk/src/main/java/cc/kune/core/shared/dto/UserDTO.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/core/shared/dto/UserDTO.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -19,142 +19,153 @@
  */
 package cc.kune.core.shared.dto;
 
+import cc.kune.core.shared.domain.dto.EmailNotificationFrequency;
+
 import com.google.gwt.user.client.rpc.IsSerializable;
 
 public class UserDTO implements IsSerializable {
-    private Long id;
-    private String name;
-    private String shortName;
-    private I18nLanguageDTO language;
-    private I18nCountryDTO country;
-    private TimeZoneDTO timezone;
-    private String password;
-    private String email;
-    private String avatar;
-    private boolean publishRoster;
-    private SubscriptionMode subscriptionMode;
-    private String chatColor;
+  private String avatar;
+  private String chatColor;
+  private I18nCountryDTO country;
+  private String email;
+  private EmailNotificationFrequency emailNotifFreq;
+  private Long id;
+  private I18nLanguageDTO language;
+  private String name;
+  private String password;
+  private boolean publishRoster;
+  private String shortName;
+  private SubscriptionMode subscriptionMode;
+  private TimeZoneDTO timezone;
 
-    public UserDTO() {
-        this(null, null, null, null, null, null, null, null, true, null, null);
-    }
+  public UserDTO() {
+    this(null, null, null, null, null, null, null, null, true, null, null);
+  }
 
-    public UserDTO(final String shortName, final String name, final String password, final String email,
-            final I18nLanguageDTO language, final I18nCountryDTO country, final TimeZoneDTO timezone,
-            final String avatar, final boolean publishRoster, final SubscriptionMode subscriptionMode,
-            final String chatColor) {
-        this.name = name;
-        this.shortName = shortName;
-        this.password = password;
-        this.email = email;
-        this.language = language;
-        this.country = country;
-        this.timezone = timezone;
-        this.avatar = avatar;
-        this.publishRoster = publishRoster;
-        this.subscriptionMode = subscriptionMode;
-        this.chatColor = chatColor;
-    }
+  public UserDTO(final String shortName, final String name, final String password, final String email,
+      final I18nLanguageDTO language, final I18nCountryDTO country, final TimeZoneDTO timezone,
+      final String avatar, final boolean publishRoster, final SubscriptionMode subscriptionMode,
+      final String chatColor) {
+    this.name = name;
+    this.shortName = shortName;
+    this.password = password;
+    this.email = email;
+    this.language = language;
+    this.country = country;
+    this.timezone = timezone;
+    this.avatar = avatar;
+    this.publishRoster = publishRoster;
+    this.subscriptionMode = subscriptionMode;
+    this.chatColor = chatColor;
+  }
 
-    public String getAvatar() {
-        return avatar;
-    }
+  public String getAvatar() {
+    return avatar;
+  }
 
-    public String getChatColor() {
-        return chatColor;
-    }
+  public String getChatColor() {
+    return chatColor;
+  }
 
-    public I18nCountryDTO getCountry() {
-        return country;
-    }
+  public I18nCountryDTO getCountry() {
+    return country;
+  }
 
-    public String getEmail() {
-        return email;
-    }
+  public String getEmail() {
+    return email;
+  }
 
-    public Long getId() {
-        return id;
-    }
+  public EmailNotificationFrequency getEmailNotifFreq() {
+    return emailNotifFreq;
+  }
 
-    public I18nLanguageDTO getLanguage() {
-        return language;
-    }
+  public Long getId() {
+    return id;
+  }
 
-    public String getName() {
-        return name;
-    }
+  public I18nLanguageDTO getLanguage() {
+    return language;
+  }
 
-    public String getPassword() {
-        return password;
-    }
+  public String getName() {
+    return name;
+  }
 
-    public String getShortName() {
-        return shortName;
-    }
+  public String getPassword() {
+    return password;
+  }
 
-    public SubscriptionMode getSubscriptionMode() {
-        return subscriptionMode;
-    }
+  public String getShortName() {
+    return shortName;
+  }
 
-    public TimeZoneDTO getTimezone() {
-        return timezone;
-    }
+  public SubscriptionMode getSubscriptionMode() {
+    return subscriptionMode;
+  }
 
-    public boolean isPublishRoster() {
-        return publishRoster;
-    }
+  public TimeZoneDTO getTimezone() {
+    return timezone;
+  }
 
-    public void setAvatar(final String avatar) {
-        this.avatar = avatar;
-    }
+  public boolean isPublishRoster() {
+    return publishRoster;
+  }
 
-    public void setChatColor(final String chatColor) {
-        this.chatColor = chatColor;
-    }
+  public void setAvatar(final String avatar) {
+    this.avatar = avatar;
+  }
 
-    public void setCountry(final I18nCountryDTO country) {
-        this.country = country;
-    }
+  public void setChatColor(final String chatColor) {
+    this.chatColor = chatColor;
+  }
 
-    public void setEmail(final String email) {
-        this.email = email;
-    }
+  public void setCountry(final I18nCountryDTO country) {
+    this.country = country;
+  }
 
-    public void setId(final Long id) {
-        this.id = id;
-    }
+  public void setEmail(final String email) {
+    this.email = email;
+  }
 
-    public void setLanguage(final I18nLanguageDTO language) {
-        this.language = language;
-    }
+  public void setEmailNotifFreq(final EmailNotificationFrequency emailNotifFreq) {
+    this.emailNotifFreq = emailNotifFreq;
+  }
 
-    public void setName(final String name) {
-        this.name = name;
-    }
+  public void setId(final Long id) {
+    this.id = id;
+  }
 
-    public void setPassword(final String password) {
-        this.password = password;
-    }
+  public void setLanguage(final I18nLanguageDTO language) {
+    this.language = language;
+  }
 
-    public void setPublishRoster(final boolean publishRoster) {
-        this.publishRoster = publishRoster;
-    }
+  public void setName(final String name) {
+    this.name = name;
+  }
 
-    public void setShortName(final String shortName) {
-        this.shortName = shortName;
-    }
+  public void setPassword(final String password) {
+    this.password = password;
+  }
 
-    public void setSubscriptionMode(final SubscriptionMode subscriptionMode) {
-        this.subscriptionMode = subscriptionMode;
-    }
+  public void setPublishRoster(final boolean publishRoster) {
+    this.publishRoster = publishRoster;
+  }
 
-    public void setTimezone(final TimeZoneDTO timezone) {
-        this.timezone = timezone;
-    }
+  public void setShortName(final String shortName) {
+    this.shortName = shortName;
+  }
 
-    @Override
-    public String toString() {
-        return "UserDTO[" + shortName + "]";
-    }
+  public void setSubscriptionMode(final SubscriptionMode subscriptionMode) {
+    this.subscriptionMode = subscriptionMode;
+  }
 
+  public void setTimezone(final TimeZoneDTO timezone) {
+    this.timezone = timezone;
+  }
+
+  @Override
+  public String toString() {
+    return "UserDTO[" + shortName + "]";
+  }
+
 }

Modified: trunk/src/main/java/cc/kune/domain/User.java
===================================================================
--- trunk/src/main/java/cc/kune/domain/User.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/domain/User.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -46,6 +46,7 @@
 import org.hibernate.validator.Pattern;
 
 import cc.kune.core.shared.domain.UserSNetVisibility;
+import cc.kune.core.shared.domain.dto.EmailNotificationFrequency;
 import cc.kune.core.shared.domain.utils.StateToken;
 import cc.kune.domain.utils.HasId;
 
@@ -54,6 +55,8 @@
 @Table(name = "kusers")
 public class User implements HasId {
 
+  private static final EmailNotificationFrequency DEF_EMAIL_FREQ = EmailNotificationFrequency.immediately;
+
   // public static final String PROPS_ID = "userprops";
   // see: http://docs.codehaus.org/display/PICO/Good+Citizen:
   // Never expect or return null
@@ -80,14 +83,17 @@
   @Length(min = 1)
   private String email;
 
+  @Enumerated(EnumType.STRING)
+  private EmailNotificationFrequency emailNotifFreq;
+
+  // @OneToOne(cascade = CascadeType.REMOVE)
+  // private final CustomProperties customProperties;
+
   @Id
   @DocumentId
   @GeneratedValue
   private Long id;
 
-  // @OneToOne(cascade = CascadeType.REMOVE)
-  // private final CustomProperties customProperties;
-
   @ManyToOne
   @NotNull
   private I18nLanguage language;
@@ -146,6 +152,7 @@
     sNetVisibility = UserSNetVisibility.anyone;
     this.createdOn = System.currentTimeMillis();
     this.lastLogin = null;
+    emailNotifFreq = DEF_EMAIL_FREQ;
     // this.properties = properties;
   }
 
@@ -204,6 +211,10 @@
   // return customProperties;
   // }
 
+  public EmailNotificationFrequency getEmailNotifFreq() {
+    return emailNotifFreq == null ? DEF_EMAIL_FREQ : emailNotifFreq;
+  }
+
   public boolean getHasLogo() {
     return hasLogo();
   }
@@ -229,14 +240,14 @@
     return password;
   }
 
+  // public Properties getProperties() {
+  // return properties;
+  // }
+
   public byte[] getSalt() {
     return salt;
   }
 
-  // public Properties getProperties() {
-  // return properties;
-  // }
-
   public String getShortName() {
     return shortName;
   }
@@ -283,6 +294,10 @@
     this.email = email;
   }
 
+  public void setEmailNotifFreq(final EmailNotificationFrequency emailNotifFreq) {
+    this.emailNotifFreq = emailNotifFreq;
+  }
+
   @Override
   public void setId(final Long id) {
     this.id = id;

Modified: trunk/src/main/java/cc/kune/events/client/actions/EventAddMenuItem.java
===================================================================
--- trunk/src/main/java/cc/kune/events/client/actions/EventAddMenuItem.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/events/client/actions/EventAddMenuItem.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -28,7 +28,7 @@
       super(AccessRolDTO.Editor, true);
       this.i18n = i18n;
       this.calendar = calendar;
-      withText(i18n.t("Add a appointment")).withIcon(res.calendarAdd());
+      withText(i18n.t("Add an appointment")).withIcon(res.calendarAdd());
     }
 
     @Override

Modified: trunk/src/main/java/cc/kune/events/client/viewer/CalendarViewerPresenter.java
===================================================================
--- trunk/src/main/java/cc/kune/events/client/viewer/CalendarViewerPresenter.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/events/client/viewer/CalendarViewerPresenter.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -139,6 +139,7 @@
         event.setCancelled(!editable);
         // event.setCancelled(true);
         NotifyUser.info("updated handler");
+        hideMenu();
       }
     });
     getView().addOpenHandler(new OpenHandler<Appointment>() {
@@ -194,7 +195,7 @@
     getView().goToday();
   }
 
-  private void hide() {
+  private void hideMenu() {
     onOverMenu.get().hide();
   }
 

Modified: trunk/src/main/java/cc/kune/wave/server/KuneAgent.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/server/KuneAgent.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/wave/server/KuneAgent.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -81,6 +81,12 @@
   }
 
   @Override
+  public WaveRef createWave(final String title, final String message, final String... participantsArray) {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  @Override
   public WaveRef createWave(final String title, final String message, final String waveIdToCopy,
       final URL gadgetUrl, final ParticipantId... participantsArray) {
     // TODO Auto-generated method stub

Modified: trunk/src/main/java/cc/kune/wave/server/KuneWaveService.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/server/KuneWaveService.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/wave/server/KuneWaveService.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -41,6 +41,8 @@
 
   WaveRef createWave(String title, String message, ParticipantId... participantsArray);
 
+  WaveRef createWave(String title, String message, String... participantsArray);
+
   WaveRef createWave(String title, String message, String waveIdToCopy, URL gadgetUrl,
       ParticipantId... participantsArray);
 

Modified: trunk/src/main/java/cc/kune/wave/server/KuneWaveServiceDefault.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/server/KuneWaveServiceDefault.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/wave/server/KuneWaveServiceDefault.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -221,6 +221,11 @@
   }
 
   @Override
+  public WaveRef createWave(final String title, final String message, final String... participantsArray) {
+    return createWave(title, message, participantUtils.listFrom(participantsArray));
+  }
+
+  @Override
   public WaveRef createWave(@Nonnull final String title, final String message,
       final String waveIdToCopy, final URL gadgetUrl, @Nonnull final ParticipantId... participantsArray) {
     String newWaveId = null;

Modified: trunk/src/main/java/cc/kune/wave/server/ParticipantUtils.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/server/ParticipantUtils.java	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/java/cc/kune/wave/server/ParticipantUtils.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -37,6 +37,7 @@
 @Singleton
 public class ParticipantUtils {
 
+  private String atDomain;
   private final String domain;
   private final ParticipantId superAdmin;
 
@@ -47,10 +48,26 @@
     superAdmin = ofImpl(databaseProperties.getAdminShortName());
   }
 
+  public String getAddressName(final String address) {
+    return address.contains(ParticipantId.DOMAIN_PREFIX) ? address.split(ParticipantId.DOMAIN_PREFIX)[0]
+        : address;
+  }
+
+  private String getAtDomain() {
+    if (atDomain == null) {
+      atDomain = ParticipantId.DOMAIN_PREFIX + domain;
+    }
+    return atDomain;
+  }
+
   public ParticipantId getSuperAdmin() {
     return superAdmin;
   }
 
+  public boolean isLocal(final String address) {
+    return address.contains(getAtDomain());
+  }
+
   public ParticipantId[] listFrom(final Set<Group> list) {
     final ParticipantId[] array = new ParticipantId[list.size()];
     final Iterator<Group> iterator = list.iterator();
@@ -60,6 +77,14 @@
     return array;
   }
 
+  public ParticipantId[] listFrom(final String... list) {
+    final ParticipantId[] array = new ParticipantId[list.length];
+    for (int i = 0; i < list.length; i++) {
+      array[i] = of(list[i]);
+    }
+    return array;
+  }
+
   public ParticipantId of(final String username) {
     return ofImpl(username);
   }
@@ -69,7 +94,7 @@
       if (username.contains(ParticipantId.DOMAIN_PREFIX)) {
         return ParticipantId.of(username);
       } else {
-        return ParticipantId.of(username + ParticipantId.DOMAIN_PREFIX + domain);
+        return ParticipantId.of(username + getAtDomain());
       }
     } catch (final InvalidParticipantAddress e) {
       throw new DefaultException("Error getting Wave participant Id");

Added: trunk/src/main/java/cc/kune/wave/server/WaveEmailNotifier.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/server/WaveEmailNotifier.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/wave/server/WaveEmailNotifier.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,75 @@
+package cc.kune.wave.server;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.waveprotocol.box.common.DeltaSequence;
+import org.waveprotocol.box.server.waveserver.WaveBus;
+import org.waveprotocol.box.server.waveserver.WaveBus.Subscriber;
+import org.waveprotocol.wave.model.id.IdUtil;
+import org.waveprotocol.wave.model.id.WaveletId;
+import org.waveprotocol.wave.model.id.WaveletName;
+import org.waveprotocol.wave.model.operation.wave.AddParticipant;
+import org.waveprotocol.wave.model.operation.wave.TransformedWaveletDelta;
+import org.waveprotocol.wave.model.operation.wave.WaveletBlipOperation;
+import org.waveprotocol.wave.model.operation.wave.WaveletOperation;
+import org.waveprotocol.wave.model.version.HashedVersion;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.model.wave.data.ReadableWaveletData;
+import org.waveprotocol.wave.model.waveref.WaveRef;
+
+import cc.kune.core.server.LogThis;
+import cc.kune.core.server.mail.MailServiceDefault.FormatedString;
+import cc.kune.core.server.notifier.NotifyService;
+import cc.kune.core.server.notifier.NotifyType;
+import cc.kune.core.server.notifier.UsersOnline;
+import cc.kune.core.server.properties.KuneBasicProperties;
+
+import com.google.inject.Inject;
+
+ at LogThis
+public class WaveEmailNotifier {
+  public static final Log LOG = LogFactory.getLog(WaveEmailNotifier.class);
+
+  @Inject
+  public WaveEmailNotifier(final WaveBus waveBus, final NotifyService notifyService,
+      final UsersOnline usersOnline, final KuneBasicProperties basicProperties,
+      final ParticipantUtils partUtils) {
+    waveBus.subscribe(new Subscriber() {
+      @Override
+      public void waveletCommitted(final WaveletName waveletName, final HashedVersion version) {
+      }
+
+      @Override
+      public void waveletUpdate(final ReadableWaveletData wavelet, final DeltaSequence deltas) {
+        final WaveletId waveletId = wavelet.getWaveletId();
+        if (IdUtil.isUserDataWavelet(waveletId)) {
+          return;
+        }
+        for (final TransformedWaveletDelta delta : deltas) {
+          for (final WaveletOperation op : delta) {
+            if (op instanceof AddParticipant) {
+              final ParticipantId user = ((AddParticipant) op).getParticipantId();
+              final String url = KuneWaveUtils.getUrl(WaveRef.of(wavelet.getWaveId(), waveletId));
+              final FormatedString body = FormatedString.build(
+                  // FIXME when SSL/ssl support
+                  "Hi there,<br><br>You have a new message in %s. <a href=\"http://%s/#%s\">Read more</a>.<br>",
+                  basicProperties.getSiteCommonName(), basicProperties.getSiteDomain(), url);
+              final String address = user.getAddress();
+              if (partUtils.isLocal(address)) {
+                final String userName = partUtils.getAddressName(address);
+                if (!usersOnline.isLogged(userName)) {
+                  notifyService.send(NotifyType.email, FormatedString.build("You have a new message"),
+                      body, true, userName);
+                  notifyService.send(NotifyType.chat, FormatedString.build("New message"), body, true,
+                      userName);
+                }
+              }
+            } else if (op instanceof WaveletBlipOperation) {
+
+            }
+          }
+        }
+      }
+    });
+  }
+}

Modified: trunk/src/main/resources/kune.properties
===================================================================
--- trunk/src/main/resources/kune.properties	2011-12-21 23:32:22 UTC (rev 1647)
+++ trunk/src/main/resources/kune.properties	2011-12-27 01:32:05 UTC (rev 1648)
@@ -27,7 +27,7 @@
 kune.site.smtp.host = localhost
 kune.site.smtp.defaultfrom = noreply at localhost
 # If we should avoid the use of smtp (use only for avoid emails during development)
-kune.site.smtp.skip = true
+kune.site.smtp.skip = false
 
 ### Database
 

Added: trunk/src/test/java/cc/kune/core/server/mail/MailServiceDefaultTest.java
===================================================================
--- trunk/src/test/java/cc/kune/core/server/mail/MailServiceDefaultTest.java	                        (rev 0)
+++ trunk/src/test/java/cc/kune/core/server/mail/MailServiceDefaultTest.java	2011-12-27 01:32:05 UTC (rev 1648)
@@ -0,0 +1,43 @@
+package cc.kune.core.server.mail;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import cc.kune.core.server.mail.MailServiceDefault.FormatedString;
+import cc.kune.core.server.properties.KunePropertiesDefault;
+
+public class MailServiceDefaultTest {
+
+  private MailServiceDefault service;
+
+  @Test(expected = NullPointerException.class)
+  public void basicException() {
+    assertEquals(null, FormatedString.build(null).getString());
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void basicExceptionWithArgs() {
+    assertEquals(null, FormatedString.build(null).getString());
+    assertEquals(null, FormatedString.build(null, "arg").getString());
+  }
+
+  @Test
+  public void basicFormat() {
+    assertEquals("basic", FormatedString.build("basic").getString());
+    assertEquals("basic arg", FormatedString.build("basic %s", "arg").getString());
+    assertEquals("basic %s", FormatedString.build("basic %s", null).getString());
+  }
+
+  @Before
+  public void before() {
+    service = new MailServiceDefault(new KunePropertiesDefault("kune.properties"));
+  }
+
+  @Test
+  public void simpleTest() {
+    service.sendPlain(FormatedString.build("Some test subject"), FormatedString.build("Some body"),
+        "vjrj at localhost");
+  }
+}




More information about the kune-commits mailing list