88import org .apache .log4j .Logger ;
99import org .buddycloud .channelserver .db .exception .NodeStoreException ;
1010import org .buddycloud .channelserver .pubsub .model .NodeItem ;
11+ import org .buddycloud .channelserver .pubsub .model .impl .GlobalItemIDImpl ;
1112import org .dom4j .Element ;
1213import org .dom4j .dom .DOMElement ;
1314import org .xmpp .packet .JID ;
14- import org .xmpp .packet .PacketError ;
1515
1616public 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