Skip to content

Commit 3d558f0

Browse files
committed
Merge pull request #129 from surevine/votes
Support voting in the java server
2 parents ff2f0e2 + a1351f4 commit 3d558f0

10 files changed

Lines changed: 648 additions & 97 deletions

File tree

src/main/java/org/buddycloud/channelserver/channel/ValidateEntry.java

Lines changed: 150 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
import org.apache.log4j.Logger;
99
import org.buddycloud.channelserver.db.exception.NodeStoreException;
1010
import org.buddycloud.channelserver.pubsub.model.NodeItem;
11+
import org.buddycloud.channelserver.pubsub.model.impl.GlobalItemIDImpl;
1112
import org.dom4j.Element;
1213
import org.dom4j.dom.DOMElement;
1314
import org.xmpp.packet.JID;
14-
import org.xmpp.packet.PacketError;
1515

1616
public class ValidateEntry {
1717

@@ -20,6 +20,14 @@ public class ValidateEntry {
2020
public static final String UNSUPPORTED_CONTENT_TYPE = "unsupported-content-type";
2121
public static final String MAX_THREAD_DEPTH_EXCEEDED = "max-thread-depth-exceeded";
2222
public static final String PARENT_ITEM_NOT_FOUND = "parent-item-not-found";
23+
public static final String TARGETED_ITEM_NOT_FOUND = "targeted-item-not-found";
24+
public static final String IN_REPLY_TO_MISSING = "missing-in-reply-to";
25+
public static final String TARGET_ELEMENT_MISSING = "missing-target";
26+
public static final String MISSING_TARGET_ID = "missing-target-id";
27+
public static final String RATING_OUT_OF_RANGE = "rating-out-of-range";
28+
public static final String INVALID_RATING_VALUE = "invalid-rating";
29+
public static final String TARGET_MUST_BE_IN_SAME_THREAD = "target-outside-thread";
30+
public static final String CAN_ONLY_RATE_A_POST = "invalid-rating-request";
2331

2432
public static final String CONTENT_TEXT = "text";
2533
public static final String CONTENT_XHTML = "xhtml";
@@ -28,6 +36,7 @@ public class ValidateEntry {
2836
public static final String NS_ACTIVITYSTREAM = "http://activitystrea.ms/spec/1.0/";
2937
public static final String NS_ATOM_THREAD = "http://purl.org/syndication/thread/1.0";
3038
public static final String NS_GEOLOCATION = "http://jabber.org/protocol/geoloc";
39+
public static final String NS_REVIEW = "http://activitystrea.ms/schema/1.0/review";
3140

3241
public static final String AUTHOR_URI_PREFIX = "acct:";
3342
public static final String AUTHOR_TYPE = "person";
@@ -36,47 +45,54 @@ public class ValidateEntry {
3645
public static final String POST_TYPE_COMMENT = "comment";
3746

3847
public static final String ACTIVITY_VERB_POST = "post";
48+
public static final String ACTIVITY_VERB_RATED = "rated";
3949

40-
private static Logger logger = Logger.getLogger(ValidateEntry.class);
50+
private static Logger LOGGER = Logger.getLogger(ValidateEntry.class);
4151

4252
private Element entry;
4353

4454
private String errorMessage = "";
45-
private String inReplyTo;
55+
private String inReplyTo = null;
56+
private String targetId = null;
57+
private int itemRating = 0;
4658
private Element meta;
4759
private Element media;
48-
60+
4961
private JID jid;
5062
private String channelServerDomain;
5163
private String node;
5264
private ChannelManager channelManager;
65+
private NodeItem replyingToItem;
66+
private NodeItem targetItem;
5367

5468
Map<String, String> params = new HashMap<String, String>();
5569

5670
private Element geoloc;
5771

58-
public ValidateEntry() {}
59-
72+
public ValidateEntry() {
73+
}
74+
6075
public ValidateEntry(Element entry) {
6176
setEntry(entry);
6277
}
63-
78+
6479
public void setEntry(Element entry) {
6580
this.entry = entry;
6681
}
6782

6883
public String getErrorMessage() {
6984
return this.errorMessage;
7085
}
71-
86+
7287
public void setChannelManager(ChannelManager channelManager) {
7388
this.channelManager = channelManager;
7489
}
7590

7691
/**
7792
* This is a big hackety-hack.
78-
* @throws InterruptedException
79-
* @throws NodeStoreException
93+
*
94+
* @throws InterruptedException
95+
* @throws NodeStoreException
8096
*/
8197
public boolean isValid() throws NodeStoreException {
8298
if (this.entry == null) {
@@ -86,15 +102,16 @@ public boolean isValid() throws NodeStoreException {
86102

87103
Element id = this.entry.element("id");
88104
if ((id == null) || (true == id.getText().isEmpty())) {
89-
if (null != id)
105+
if (null != id) {
90106
id.detach();
91-
logger.debug("ID of the entry was missing. We add a default one to it: 1");
107+
}
108+
LOGGER.debug("ID of the entry was missing. We add a default one to it: 1");
92109
this.entry.addElement("id").setText("1");
93110
}
94111

95112
Element title = this.entry.element("title");
96113
if (null == title) {
97-
logger.debug("Title of the entry was missing. We add a default one to it: 'Post'.");
114+
LOGGER.debug("Title of the entry was missing. We add a default one to it: 'Post'.");
98115
title = this.entry.addElement("title");
99116
title.setText("Post");
100117
}
@@ -121,15 +138,22 @@ public boolean isValid() throws NodeStoreException {
121138
if (null == updated) {
122139

123140
String updateTime = Conf.formatDate(new Date());
124-
logger.debug("Update of the entry was missing. We add a default one to it: '"
141+
LOGGER.debug("Update of the entry was missing. We add a default one to it: '"
125142
+ updateTime + "'.");
126143
this.entry.addElement("updated").setText(updateTime);
127144
}
128145

129146
this.geoloc = this.entry.element("geoloc");
130147

131-
Element reply = this.entry.element("in-reply-to");
132-
if ((null != reply) && (false == validateInReplyToElement(reply))) {
148+
if (false == validateInReplyToElement(this.entry.element("in-reply-to"))) {
149+
return false;
150+
}
151+
152+
if (false == validateTargetElement(this.entry.element("target"))) {
153+
return false;
154+
}
155+
156+
if (false == validateRatingElement(this.entry.element("rating"))) {
133157
return false;
134158
}
135159

@@ -159,12 +183,8 @@ public Element getPayload() {
159183
entry.addElement("id").setText(
160184
"tag:" + channelServerDomain + "," + node + "," + id);
161185

162-
entry.addElement("title").setText(this.params.get("title"));
163-
164-
Element content = entry.addElement("content");
165-
content.setText(this.params.get("content"));
166-
content.addAttribute("type", this.params.get("content-type"));
167-
186+
String title = this.params.get("title");
187+
String itemContent = this.params.get("content");
168188
String publishedDate = Conf.formatDate(new Date());
169189

170190
entry.addElement("published").setText(publishedDate);
@@ -192,11 +212,6 @@ public Element getPayload() {
192212

193213
this.geoloc = this.entry.element("geoloc");
194214

195-
entry.addElement("activity:verb").setText(activityVerb);
196-
197-
Element activityObject = entry.addElement("activity:object");
198-
activityObject.addElement("activity:object-type").setText(postType);
199-
200215
if (null != meta) {
201216
entry.add(meta.createCopy());
202217
}
@@ -205,6 +220,33 @@ public Element getPayload() {
205220
entry.add(media.createCopy());
206221
}
207222

223+
if (null != targetId) {
224+
GlobalItemIDImpl globalTargetId = new GlobalItemIDImpl(new JID(
225+
channelServerDomain), node, targetId);
226+
Element target = entry.addElement("activity:target");
227+
target.addElement("id").setText(globalTargetId.toString());
228+
target.addElement("activity:object-type").setText("post");
229+
}
230+
231+
if (itemRating > 0) {
232+
entry.addNamespace("review", NS_REVIEW);
233+
String rating = String.format("%d.0", itemRating);
234+
entry.addElement("review:rating").setText(rating);
235+
title = "Rating";
236+
itemContent = "rating:" + rating;
237+
activityVerb = ACTIVITY_VERB_RATED;
238+
}
239+
240+
entry.addElement("title").setText(title);
241+
242+
Element content = entry.addElement("content");
243+
content.setText(itemContent);
244+
content.addAttribute("type", this.params.get("content-type"));
245+
246+
entry.addElement("activity:verb").setText(activityVerb);
247+
Element activityObject = entry.addElement("activity:object");
248+
activityObject.addElement("activity:object-type").setText(postType);
249+
208250
return entry;
209251
}
210252

@@ -219,30 +261,99 @@ public void setNode(String node) {
219261
public void setTo(String channelServerDomain) {
220262
this.channelServerDomain = channelServerDomain;
221263
}
222-
223264

224-
private boolean validateInReplyToElement(Element reply) throws NodeStoreException {
265+
private boolean validateInReplyToElement(Element reply)
266+
throws NodeStoreException {
267+
if (null == reply) {
268+
return true;
269+
}
225270

226271
inReplyTo = reply.attributeValue("ref");
227-
if (-1 != inReplyTo.indexOf(",")) {
228-
String[] tokens = inReplyTo.split(",");
229-
inReplyTo = tokens[2];
272+
if (true == GlobalItemIDImpl.isGlobalId(inReplyTo)) {
273+
inReplyTo = GlobalItemIDImpl.toLocalId(inReplyTo);
230274
}
231275

232-
String[] inReplyToParts = reply.attributeValue("ref").split(",");
233-
inReplyTo = inReplyToParts[inReplyToParts.length - 1];
234-
235-
NodeItem nodeItem = null;
236-
if (null == (nodeItem = channelManager.getNodeItem(node, inReplyTo))) {
276+
replyingToItem = channelManager.getNodeItem(node,
277+
inReplyTo);
278+
if (null == replyingToItem) {
237279
this.errorMessage = PARENT_ITEM_NOT_FOUND;
238280
return false;
239281
}
240-
if (null != nodeItem.getInReplyTo()) {
241-
logger.error("User is attempting to reply to a reply");
282+
if (null != replyingToItem.getInReplyTo()) {
283+
LOGGER.error("User is attempting to reply to a reply");
242284
this.errorMessage = MAX_THREAD_DEPTH_EXCEEDED;
243285
return false;
244286
}
245287
return true;
246288
}
247289

290+
private boolean validateTargetElement(Element target)
291+
throws NodeStoreException {
292+
if (null == target) {
293+
return true;
294+
}
295+
targetId = target.elementText("id");
296+
if ((null == targetId) || (0 == targetId.length())) {
297+
this.errorMessage = MISSING_TARGET_ID;
298+
return false;
299+
}
300+
if (null == inReplyTo) {
301+
this.errorMessage = IN_REPLY_TO_MISSING;
302+
return false;
303+
}
304+
if (true == GlobalItemIDImpl.isGlobalId(targetId)) {
305+
targetId = GlobalItemIDImpl.toLocalId(targetId);
306+
}
307+
if (true == targetId.equals(replyingToItem.getId())) {
308+
targetItem = replyingToItem;
309+
} else {
310+
targetItem = channelManager.getNodeItem(node, targetId);
311+
}
312+
if (null == targetItem) {
313+
this.errorMessage = TARGETED_ITEM_NOT_FOUND;
314+
return false;
315+
}
316+
if (true == targetItem.getId().equals(targetId)) {
317+
return true;
318+
}
319+
if ((null == targetItem.getInReplyTo())
320+
|| (false == targetItem.getInReplyTo().equals(targetId))) {
321+
this.errorMessage = TARGET_MUST_BE_IN_SAME_THREAD;
322+
return false;
323+
}
324+
return true;
325+
}
326+
327+
private boolean validateRatingElement(Element rating) {
328+
if (null == rating) {
329+
return true;
330+
}
331+
if (null == inReplyTo) {
332+
this.errorMessage = IN_REPLY_TO_MISSING;
333+
return false;
334+
}
335+
if (null == targetId) {
336+
this.errorMessage = TARGET_ELEMENT_MISSING;
337+
return false;
338+
}
339+
if (targetItem.getPayload().indexOf(NS_REVIEW) > -1) {
340+
this.errorMessage = CAN_ONLY_RATE_A_POST;
341+
return false;
342+
}
343+
try {
344+
double itemRatingFloat = Double.parseDouble(rating.getTextTrim());
345+
if (itemRatingFloat != Math.floor(itemRatingFloat)) {
346+
throw new NumberFormatException("Non-integer rating provided");
347+
}
348+
itemRating = (int) itemRatingFloat;
349+
} catch (NumberFormatException e) {
350+
this.errorMessage = INVALID_RATING_VALUE;
351+
return false;
352+
}
353+
if ((itemRating < 1) || (itemRating > 5)) {
354+
this.errorMessage = RATING_OUT_OF_RANGE;
355+
return false;
356+
}
357+
return true;
358+
}
248359
}

0 commit comments

Comments
 (0)