diff options
| author | Jesse Morgan <jesse@jesterpm.net> | 2014-02-22 18:15:15 -0800 | 
|---|---|---|
| committer | Jesse Morgan <jesse@jesterpm.net> | 2014-02-22 18:15:15 -0800 | 
| commit | 42503d407a5ec4efdb61a4d009f1bd46ffc8a965 (patch) | |
| tree | 59933de4d81225f748a218f84c80f5e3edea77ce /src | |
| parent | 9526fe75f2343e50f35c436dc0b4a4beb7de7eff (diff) | |
Adding support to post new messages.
Other Changes:
* JsonEncodedProvider no longer implements Provider.
* Only the first answer is shown. Others slide down.
* Switch going deeper and the feed.
Diffstat (limited to 'src')
| -rw-r--r-- | src/com/p4square/grow/backend/db/CassandraProviderImpl.java | 3 | ||||
| -rw-r--r-- | src/com/p4square/grow/backend/feed/TopicResource.java | 8 | ||||
| -rw-r--r-- | src/com/p4square/grow/frontend/ErrorPage.java | 3 | ||||
| -rw-r--r-- | src/com/p4square/grow/frontend/FeedData.java | 31 | ||||
| -rw-r--r-- | src/com/p4square/grow/frontend/FeedResource.java | 107 | ||||
| -rw-r--r-- | src/com/p4square/grow/frontend/GrowFrontend.java | 2 | ||||
| -rw-r--r-- | src/com/p4square/grow/frontend/JsonRequestProvider.java | 24 | ||||
| -rw-r--r-- | src/com/p4square/grow/model/Message.java | 6 | ||||
| -rw-r--r-- | src/com/p4square/grow/model/UserRecord.java | 93 | ||||
| -rw-r--r-- | src/com/p4square/grow/provider/JsonEncodedProvider.java | 2 | ||||
| -rw-r--r-- | src/com/p4square/grow/tools/AssessmentStats.java | 3 | ||||
| -rw-r--r-- | src/templates/templates/communityfeed.ftl | 10 | ||||
| -rw-r--r-- | src/templates/templates/training.ftl | 8 | 
13 files changed, 276 insertions, 24 deletions
diff --git a/src/com/p4square/grow/backend/db/CassandraProviderImpl.java b/src/com/p4square/grow/backend/db/CassandraProviderImpl.java index c1f6e6d..da5a9f2 100644 --- a/src/com/p4square/grow/backend/db/CassandraProviderImpl.java +++ b/src/com/p4square/grow/backend/db/CassandraProviderImpl.java @@ -6,6 +6,7 @@ package com.p4square.grow.backend.db;  import java.io.IOException; +import com.p4square.grow.provider.Provider;  import com.p4square.grow.provider.JsonEncodedProvider;  /** @@ -13,7 +14,7 @@ import com.p4square.grow.provider.JsonEncodedProvider;   *   * @author Jesse Morgan <jesse@jesterpm.net>   */ -public class CassandraProviderImpl<V> extends JsonEncodedProvider<CassandraKey, V> { +public class CassandraProviderImpl<V> extends JsonEncodedProvider<V> implements Provider<CassandraKey, V> {      private final CassandraDatabase mDb;      public CassandraProviderImpl(CassandraDatabase db, Class<V> clazz) { diff --git a/src/com/p4square/grow/backend/feed/TopicResource.java b/src/com/p4square/grow/backend/feed/TopicResource.java index 3519ca9..5826355 100644 --- a/src/com/p4square/grow/backend/feed/TopicResource.java +++ b/src/com/p4square/grow/backend/feed/TopicResource.java @@ -76,8 +76,12 @@ public class TopicResource extends ServerResource {          try {              // Deserialize the incoming message. -            JacksonRepresentation<Message> jsonRep = new JacksonRepresentation<Message>(entity, Message.class); -            Message message = jsonRep.getObject(); +            JacksonRepresentation<MessageThread> jsonRep = +                new JacksonRepresentation<MessageThread>(entity, MessageThread.class); + +            // Get the message from the request. +            // Throw away the wrapping MessageThread because we'll create our own later. +            Message message = jsonRep.getObject().getMessage();              if (message.getCreated() == null) {                  message.setCreated(new Date());              } diff --git a/src/com/p4square/grow/frontend/ErrorPage.java b/src/com/p4square/grow/frontend/ErrorPage.java index 39aae93..61f6a35 100644 --- a/src/com/p4square/grow/frontend/ErrorPage.java +++ b/src/com/p4square/grow/frontend/ErrorPage.java @@ -36,6 +36,9 @@ public class ErrorPage extends WriterRepresentation {      public static final ErrorPage BACKEND_ERROR =          new ErrorPage("Error communicating with backend."); +    public static final ErrorPage NOT_FOUND = +        new ErrorPage("The requested URL could not be found."); +      private static Template cTemplate = null;      private static Map<String, Object> cRoot = null; diff --git a/src/com/p4square/grow/frontend/FeedData.java b/src/com/p4square/grow/frontend/FeedData.java index acff8d9..eddc6a4 100644 --- a/src/com/p4square/grow/frontend/FeedData.java +++ b/src/com/p4square/grow/frontend/FeedData.java @@ -28,8 +28,12 @@ public class FeedData {      private final Config mConfig;      private final String mBackendURI; -    private final Provider<String, List<MessageThread>> mThreadProvider; -    private final Provider<String, List<Message>> mMessageProvider; +    // TODO: Elegantly merge the List and individual providers. +    private final JsonRequestProvider<List<MessageThread>> mThreadsProvider; +    private final JsonRequestProvider<MessageThread> mThreadProvider; + +    private final JsonRequestProvider<List<Message>> mMessagesProvider; +    private final JsonRequestProvider<Message> mMessageProvider;      public FeedData(final Context context, final Config config) {          mConfig = config; @@ -40,18 +44,33 @@ public class FeedData {          TypeFactory factory = JsonEncodedProvider.MAPPER.getTypeFactory();          JavaType threadType = factory.constructCollectionType(List.class, MessageThread.class); -        mThreadProvider = new JsonRequestProvider<List<MessageThread>>(clientDispatcher, threadType); +        mThreadsProvider = new JsonRequestProvider<List<MessageThread>>(clientDispatcher, threadType); +        mThreadProvider = new JsonRequestProvider<MessageThread>(clientDispatcher, MessageThread.class);          JavaType messageType = factory.constructCollectionType(List.class, Message.class); -        mMessageProvider = new JsonRequestProvider<List<Message>>(clientDispatcher, messageType); +        mMessagesProvider = new JsonRequestProvider<List<Message>>(clientDispatcher, messageType); +        mMessageProvider = new JsonRequestProvider<Message>(clientDispatcher, Message.class);      }      public List<MessageThread> getThreads(final String topic) throws IOException { -        return mThreadProvider.get(makeUrl(topic)); +        return mThreadsProvider.get(makeUrl(topic));      }      public List<Message> getMessages(final String topic, final String threadId) throws IOException { -        return mMessageProvider.get(makeUrl(topic, threadId)); +        return mMessagesProvider.get(makeUrl(topic, threadId)); +    } + +    public void createThread(final String topic, final Message message) throws IOException { +        MessageThread thread = new MessageThread(); +        thread.setMessage(message); + +        mThreadProvider.post(makeUrl(topic), thread); +    } + +    public void createResponse(final String topic, final String thread, final Message message) +        throws IOException { + +        mMessageProvider.post(makeUrl(topic, thread), message);      }      private String makeUrl(String... parts) { diff --git a/src/com/p4square/grow/frontend/FeedResource.java b/src/com/p4square/grow/frontend/FeedResource.java new file mode 100644 index 0000000..eea89b1 --- /dev/null +++ b/src/com/p4square/grow/frontend/FeedResource.java @@ -0,0 +1,107 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.grow.frontend; + +import java.io.IOException; + +import java.util.Arrays; +import java.util.HashSet; + +import org.restlet.data.Form; +import org.restlet.data.Status; +import org.restlet.representation.Representation; +import org.restlet.resource.ServerResource; + +import org.apache.log4j.Logger; + +import com.p4square.grow.config.Config; +import com.p4square.grow.model.Message; +import com.p4square.grow.model.UserRecord; + +/** + * This resource handles user interactions with the feed. + */ +public class FeedResource extends ServerResource { +    private static final Logger LOG = Logger.getLogger(FeedResource.class); + +    private static final HashSet<String> TOPICS = new HashSet(Arrays.asList("introduction", "seeker", "believer", +            "disciple", "teacher")); + +    private Config mConfig; + +    private FeedData mFeedData; + +    // Fields pertaining to this request. +    protected String mTopic; +    protected String mThread; + +    @Override +    public void doInit() { +        super.doInit(); + +        GrowFrontend growFrontend = (GrowFrontend) getApplication(); +        mConfig = growFrontend.getConfig(); + +        mFeedData = new FeedData(getContext(), mConfig); + +        mTopic = getAttribute("topic"); +        if (mTopic != null) { +            mTopic = mTopic.trim(); +        } + +        mThread = getAttribute("thread"); +        if (mThread != null) { +            mThread = mThread.trim(); +        } +    } + +    /** +     * Create a new MessageThread. +     */ +    @Override +    protected Representation post(Representation entity) { +        try { +            if (mTopic == null || mTopic.length() == 0 || !TOPICS.contains(mTopic)) { +                setStatus(Status.CLIENT_ERROR_NOT_FOUND); +                return ErrorPage.NOT_FOUND; +            } + +            Form form = new Form(entity); + +            String question = form.getFirstValue("question"); + +            Message message = new Message(); +            message.setMessage(question); + +            UserRecord user = new UserRecord(getRequest().getClientInfo().getUser()); +            message.setAuthor(user); + +            if (mThread != null && mThread.length() != 0) { +                // Post a response +                mFeedData.createResponse(mTopic, mThread, message); + +            } else { +                // Post a new thread +                mFeedData.createThread(mTopic, message); +            } + +            /* +             * Can't trust the referrer, so we'll send them to the +             * appropriate part of the training page +             * TODO: This could be better done. +             */ +            String nextPage = mConfig.getString("dynamicRoot", ""); +            nextPage += "/account/training/" + mTopic; +            getResponse().redirectSeeOther(nextPage); +            return null; + +        } catch (IOException e) { +            LOG.fatal("Could not save message: " + e.getMessage(), e); +            setStatus(Status.SERVER_ERROR_INTERNAL); +            return ErrorPage.BACKEND_ERROR; + +        } +    } +} diff --git a/src/com/p4square/grow/frontend/GrowFrontend.java b/src/com/p4square/grow/frontend/GrowFrontend.java index 4f67afb..ac323ec 100644 --- a/src/com/p4square/grow/frontend/GrowFrontend.java +++ b/src/com/p4square/grow/frontend/GrowFrontend.java @@ -111,6 +111,8 @@ public class GrowFrontend extends FMFacade {          accountRouter.attach("/training/leader", GroupLeaderTrainingPageResource.class);          accountRouter.attach("/training/{chapter}", TrainingPageResource.class);          accountRouter.attach("/training", TrainingPageResource.class); +        accountRouter.attach("/feed/{topic}", FeedResource.class); +        accountRouter.attach("/feed/{topic}/{thread}", FeedResource.class);          final Authenticator accountGuard = createAuthenticatorChain(accountRouter);          router.attach("/account", accountGuard); diff --git a/src/com/p4square/grow/frontend/JsonRequestProvider.java b/src/com/p4square/grow/frontend/JsonRequestProvider.java index 8eee6d3..a04294d 100644 --- a/src/com/p4square/grow/frontend/JsonRequestProvider.java +++ b/src/com/p4square/grow/frontend/JsonRequestProvider.java @@ -16,6 +16,7 @@ import org.restlet.data.Status;  import org.restlet.representation.Representation;  import org.restlet.representation.StringRepresentation; +import com.p4square.grow.provider.Provider;  import com.p4square.grow.provider.JsonEncodedProvider;  /** @@ -23,7 +24,7 @@ import com.p4square.grow.provider.JsonEncodedProvider;   *   * @author Jesse Morgan <jesse@jesterpm.net>   */ -public class JsonRequestProvider<V> extends JsonEncodedProvider<String, V> { +public class JsonRequestProvider<V> extends JsonEncodedProvider<V> implements Provider<String, V> {      private final Restlet mDispatcher; @@ -66,7 +67,26 @@ public class JsonRequestProvider<V> extends JsonEncodedProvider<String, V> {          if (!response.getStatus().isSuccess()) {              throw new IOException("Could not put object. " + response.getStatus());          } -      } +    /** +     * Variant of put() which makes a POST request to the url. +     * +     * This method may eventually be incorporated into Provider for +     * creating new objects with auto-generated IDs. +     * +     * @param url The url to make the request to. +     * @param obj The post to post. +     * @throws IOException on failure. +     */ +    public void post(String url, V obj) throws IOException { +        final Request request = new Request(Method.POST, url); +        request.setEntity(new StringRepresentation(encode(obj))); + +        final Response response = mDispatcher.handle(request); + +        if (!response.getStatus().isSuccess()) { +            throw new IOException("Could not put object. " + response.getStatus()); +        } +    }  } diff --git a/src/com/p4square/grow/model/Message.java b/src/com/p4square/grow/model/Message.java index 6e07150..ad02af9 100644 --- a/src/com/p4square/grow/model/Message.java +++ b/src/com/p4square/grow/model/Message.java @@ -14,7 +14,7 @@ import java.util.Date;  public class Message {      private String mThreadId;      private String mId; -    private String mAuthor; +    private UserRecord mAuthor;      private Date mCreated;      private String mMessage; @@ -51,7 +51,7 @@ public class Message {      /**       * @return The author of the message.       */ -    public String getAuthor() { +    public UserRecord getAuthor() {          return mAuthor;      } @@ -59,7 +59,7 @@ public class Message {       * Set the author of the message.       * @param author The new author.       */ -    public void setAuthor(String author) { +    public void setAuthor(UserRecord author) {          mAuthor = author;      } diff --git a/src/com/p4square/grow/model/UserRecord.java b/src/com/p4square/grow/model/UserRecord.java new file mode 100644 index 0000000..0702eb1 --- /dev/null +++ b/src/com/p4square/grow/model/UserRecord.java @@ -0,0 +1,93 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.grow.model; + +import org.restlet.security.User; + +/** + * A simple user representation without any secrets. + */ +public class UserRecord { +    private String mId; +    private String mFirstName; +    private String mLastName; +    private String mEmail; + +    /** +     * Create an empty UserRecord. +     */ +    public UserRecord() { +    } + +    /** +     * Create a new UserRecord with the information from a User. +     */ +    public UserRecord(final User user) { +        mId = user.getIdentifier(); +        mFirstName = user.getFirstName(); +        mLastName = user.getLastName(); +        mEmail = user.getEmail(); +    } + +    /** +     * @return The user's identifier. +     */ +    public String getId() { +        return mId; +    } + +    /** +     * Set the user's identifier. +     * @param value The new id. +     */ +    public void setId (final String value) { +        mId = value; +    } + +    /** +     * @return The user's email. +     */ +    public String getEmail() { +        return mEmail; +    } + +    /** +     * Set the user's email. +     * @param value The new email. +     */ +    public void setEmail (final String value) { +        mEmail = value; +    } + +    /** +     * @return The user's first name. +     */ +    public String getFirstName() { +        return mFirstName; +    } + +    /** +     * Set the user's first name. +     * @param value The new first name. +     */ +    public void setFirstName (final String value) { +        mFirstName = value; +    } + +    /** +     * @return The user's last name. +     */ +    public String getLastName() { +        return mLastName; +    } + +    /** +     * Set the user's last name. +     * @param value The new last name. +     */ +    public void setLastName (final String value) { +        mLastName = value; +    } +} diff --git a/src/com/p4square/grow/provider/JsonEncodedProvider.java b/src/com/p4square/grow/provider/JsonEncodedProvider.java index 03e4056..7ae3f71 100644 --- a/src/com/p4square/grow/provider/JsonEncodedProvider.java +++ b/src/com/p4square/grow/provider/JsonEncodedProvider.java @@ -17,7 +17,7 @@ import com.fasterxml.jackson.databind.SerializationFeature;   *   * @author Jesse Morgan <jesse@jesterpm.net>   */ -public abstract class JsonEncodedProvider<K, V> implements Provider<K, V> { +public abstract class JsonEncodedProvider<V> {      public static final ObjectMapper MAPPER = new ObjectMapper();      static {          MAPPER.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); diff --git a/src/com/p4square/grow/tools/AssessmentStats.java b/src/com/p4square/grow/tools/AssessmentStats.java index c06e853..ca83411 100644 --- a/src/com/p4square/grow/tools/AssessmentStats.java +++ b/src/com/p4square/grow/tools/AssessmentStats.java @@ -20,6 +20,7 @@ import com.p4square.grow.model.Answer;  import com.p4square.grow.model.Question;  import com.p4square.grow.model.RecordedAnswer;  import com.p4square.grow.model.Score; +import com.p4square.grow.provider.Provider;  import com.p4square.grow.provider.JsonEncodedProvider;  /** @@ -149,7 +150,7 @@ public class AssessmentStats {          doFindHighestFromId(questions, q.getNextQuestion(), scores, path);      } -    private static class FileQuestionProvider extends JsonEncodedProvider<String, Question> { +    private static class FileQuestionProvider extends JsonEncodedProvider<Question> implements Provider<String, Question> {          private String mBaseDir;          public FileQuestionProvider(String directory) { diff --git a/src/templates/templates/communityfeed.ftl b/src/templates/templates/communityfeed.ftl index b80ef9c..632a201 100644 --- a/src/templates/templates/communityfeed.ftl +++ b/src/templates/templates/communityfeed.ftl @@ -10,22 +10,24 @@                  <div><a class="reply" href="#" onclick="answerQuestion('${thread.id}'); return false;">Answer</a></div>              </div>              <div class="answer hidden" id="answer-${thread.id}"> -                <form action="${dynamicRoot}/feed/${chapter}/${thread.id}" method="post"> +                <form action="${dynamicRoot}/account/feed/${chapter}/${thread.id}" method="post">                      <textarea name="question" rows="5" defaultValue="Write your reply."></textarea>                      <div><a class="send" href="#" onclick="$(this).closest('form').submit(); return false;">Send</a></div>                  </form>              </div>              <#list messages as msg> -                <div class="answer" id="${msg.id}"> +                <div class="answer slider" id="${msg.id}">                      A: ${msg.message!""} -                    <#--<a class="readmore" href="#">(continue)</a>--> +                    <#if msg_has_next && msg_index == 0> +                        <div><a class="readmore" href="#" onclick="showAnswers(this); return false;">(show more)</a></div> +                    </#if>                  </div>              </#list>          </article>      </#list>      <article>          <div class="question"> -            <form action="${dynamicRoot}/feed/${chapter}" name="newquestion" method="post"> +            <form action="${dynamicRoot}/account/feed/${chapter}" name="newquestion" method="post">                  <textarea name="question" rows="5" defaultValue="Ask your own question."></textarea>                  <div><a class="send" href="#" onclick="$(this).closest('form').submit(); return false;">Send</a></div>              </form> diff --git a/src/templates/templates/training.ftl b/src/templates/templates/training.ftl index 65ca5d9..ca1ebbd 100644 --- a/src/templates/templates/training.ftl +++ b/src/templates/templates/training.ftl @@ -66,10 +66,6 @@          </#list>          </div> -        <#if showfeed!false> -            <#include "/templates/communityfeed.ftl"> -        </#if> -          <#if deeperinclude?has_content>              <div id="deeper">                  <h2>Going Deeper</h2> @@ -81,6 +77,10 @@                  <#include deeperinclude>              </div>          </#if> + +        <#if showfeed!false> +            <#include "/templates/communityfeed.ftl"> +        </#if>      </div>      <div id="videoplayer">  | 
