001/* ******************************************************************************
002 * Copyright (c) 2002-2011  econda GmbH Karlsruhe
003 * All rights reserved.
004 * <p/>
005 * econda GmbH
006 * Eisenlohrstr. 43
007 * 76135 Karlsruhe
008 * Tel. +49 (721) 663035-35
009 * support@econda.de
010 * <p/>
011 * Redistribution and use in source and binary forms, with or without modification,
012 * are permitted provided that the following conditions are met:
013 * <p/>
014 * * Redistributions of source code must retain the above copyright notice,
015 * this list of conditions and the following disclaimer.
016 * * Redistributions in binary form must reproduce the above copyright notice,
017 * this list of conditions and the following disclaimer in the documentation
018 * and/or other materials provided with the distribution.
019 * * Neither the name of the ECONDA GmbH nor the names of its contributors may
020 * be used to endorse or promote products derived from this software without
021 * specific prior written permission.
022 * <p/>
023 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
024 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
025 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
026 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
027 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
028 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
029 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
030 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
031 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
032 * OF THE POSSIBILITY OF SUCH DAMAGE.
033 *******************************************************************************/
034package de.econda.droid;
035
036import org.json.JSONArray;
037import org.json.JSONException;
038import org.json.JSONObject;
039
040import de.econda.droid.impl.SimpleDigest;
041
042
043/**
044 * <p>This is the representation of a view.</p>
045 *
046 * <p>After creation of a new PageView, you can add several properties.</p>
047 *
048 * <p>Afterwards you should add the pageView to a Session and perhaps submit the collected data of the session.</p>
049 *
050 *
051 */
052public class PageView {
053
054    protected final JSONObject properties;
055
056
057    public PageView() {
058        this.properties = new JSONObject();
059    }
060
061    public PageView(JSONObject properties) {
062        this.properties = properties;
063    }
064
065    /**
066     * Adds a custom String to the properties
067     *
068     * @param key
069     * @param value
070     * @return PageView
071     */
072    public PageView addProperty(String key, String value) {
073        try {
074            properties.put(key, value);
075            return this;
076        } catch (JSONException e) {
077            throw new RuntimeException(e);
078        }
079    }
080
081    /**
082     * Adds a custom Number to the properties
083     *
084     * @param key
085     * @param value
086     * @return PageView
087     */
088    public PageView addProperty(String key, Number value) {
089        try {
090            properties.put(key, value);
091            return this;
092        } catch (JSONException e) {
093            throw new RuntimeException(e);
094        }
095    }
096
097    /**
098     * Adds a custom Boolean to the properties
099     *
100     * @param key
101     * @param value
102     * @return PageView
103     */
104    public PageView addProperty(String key, Boolean value) {
105        try {
106            properties.put(key, value);
107            return this;
108        } catch (JSONException e) {
109            throw new RuntimeException(e);
110        }
111
112    }
113
114    /**
115     * Adds a custom jsonArray to the properties
116     *
117     * @param key
118     * @param jsonArray
119     * @return PageView
120     */
121    public PageView addProperty(String key, JSONArray jsonArray) {
122        try {
123            properties.put(key, jsonArray);
124            return this;
125        } catch (JSONException e) {
126            throw new RuntimeException(e);
127        }
128
129    }
130
131    /**
132     * Adds a custom jsonObject to the properties
133     *
134     * @param key
135     * @param jsonObject
136     * @return PageView
137     */
138    public PageView addProperty(String key, JSONObject jsonObject) {
139        try {
140            properties.put(key, jsonObject);
141            return this;
142        } catch (JSONException e) {
143            throw new RuntimeException(e);
144        }
145    }
146
147
148    /**
149     * Adds a contact property
150     *
151     * @param contactType type of the contact-form used
152     * @return PageView
153     */
154    public PageView addContact(String contactType) {
155        return addProperty("scontact", contactType);
156    }
157
158    /**
159     * Adds a content property
160     *
161     * @param content A content-label for this page
162     * @return PageView
163     */
164    public PageView addContent(String content) {
165        return addProperty("content", content);
166    }
167
168    /**
169     *
170     * Adds a property for marketing channel ('source').<br>
171     * Default value is 'mobile'.<br>
172     * So your traffic is displayed in chapter 'Mobile' in econda Monitor.<br>
173     * <br>
174     * Please send additional information with the appropriate property.<br>
175     * <br>
176     * For the newsletter channel 'nl' please use property 'newsletter' for submitting additional information<br>
177     * pageView.addMarketingChannel('nl');<br>
178     * pageView.addProperty('newsletter','newsletter name');<br>
179     * <br>
180     * For the keyword channel 'kw' please use property 'adword' for submitting additional information.<br>
181     * pageView.addMarketingChannel('kw');<br>
182     * pageView.addProperty('adword','keyword campaign name');<br>
183     * <br>
184     * For the campaign channel 'cp' and all other channels please use property 'campaign' for submitting additional information.<br>
185     * pageView.addMarketingChannel('cp');<br>
186     * pageView.addProperty('campaign','campaign name');<br>
187     * <br>
188     * MarketingChannel is only stored on first page view of a session.<br>
189     * All subsequent page views in same session inherit the channel of the first page view.<br>
190     * <br>
191     * Marketingchannel 'mobile' is not configured by default in econda Analytics.<br>
192     * Please contact support, if you want to use the mobile channel.<br>
193     *
194     * @param source A marketing channel
195     * @return PageView
196     */
197    public PageView addMarketingChannel(String source) {
198        return addProperty("source", source);
199    }
200
201    /**
202     * Adds a campaign property
203     *
204     * campaign is only stored on first page view of a session.
205     * All subsequent page views in same session inherit the campaign of the first page view.
206
207     * @param campaign A campaign name
208     * @return PageView
209     */
210    public PageView addCampaign(String campaign) {
211        return addProperty("campaign", campaign);
212    }
213
214    /**
215     * Adds a download property
216     *
217     * @param label a download-label for this page
218     * @return PageView
219     */
220    public PageView addDownload(String label) {
221        return addProperty("download", label);
222    }
223
224
225    /**
226     * Adds a billing property
227     *
228     * @param billId unique identifier for this bill
229     * @param customerId unique identifier for this customer
230     * @param total the total sum of this bill!
231     * @param country the country of the customer
232     * @param cip the cip of the customer
233     * @param city the city of the customer
234     * @return PageView
235     */
236    public PageView addBill(String billId,
237                            String customerId, Double total, String country,
238                            String cip, String city) {
239
240        JSONArray lineValues = new JSONArray();
241
242        lineValues.put(billId);
243        lineValues.put(customerId);
244        lineValues.put(getBillLocationString(country, cip, city));
245        lineValues.put(total);
246        return addProperty("billing", lineValues);
247    }
248
249
250    private static String getBillLocationString(String country, String cip, String city) {
251        StringBuilder location = new StringBuilder();
252
253        if (country != null) {
254            location.append(country);
255            location.append("/");
256        }
257
258        if ((cip != null) && (cip.length() > 2)) {
259            location.append(cip.charAt(0));
260            location.append("/");
261            location.append(cip.substring(0, 2));
262            location.append("/");
263        }
264
265        if (city != null) {
266            location.append(city);
267            location.append("/");
268        }
269
270        if (cip != null) {
271            location.append(cip);
272        }
273
274        if (location.length() == 0) {
275            return "NULL";
276        }
277
278        return location.toString();
279    }
280
281
282    /**
283     * Adds a login property.<br>
284     *
285     * This method always scramble the userId with a MD5 Hash for you.
286     * This is perhaps necessary to fulfill the German "Datenschutzgesetz".
287     *
288     * @param userId UserID or email - must be a unique identifier
289     * @param resultCode 0 if success, positive int for error numbers
290     *
291     * @return PageView
292     */
293    public PageView addLogin(String userId, int resultCode) {
294        return addLogin(userId, resultCode, true);
295    }
296
297
298    /**
299     * Adds a login property.<br>
300     *
301     * Set Parameter scramble to true, if you want that this library is hashing the userId for you.
302     * This is perhaps necessary to fulfill the German "Datenschutzgesetz".
303     *
304     * @param userId UserID or email - must be a unique identifier
305     * @param resultCode 0 if success, positive int for error numbers
306     * @param scramble whether to use hashed userId or plain userId
307     *
308     * @return PageView
309     */
310    public PageView addLogin(String userId, int resultCode, boolean scramble) {
311        if (scramble){
312            userId = SimpleDigest.digest(userId);
313        }
314
315        JSONArray array = new JSONArray();
316        array.put(userId);
317        array.put(resultCode);
318        return addProperty("login", array);
319    }
320
321
322    /**
323     * Adds a target property.<br><br>
324     *
325     * @param target Name for this Target, e.g. download, lottery
326     * @param additionalInfo additional Information, e.g. the Name of the downloaded Document
327     * @param score Your value of this target (used for calculation of conversationrate)
328     * @param rule Method to use. Only first of session, all or usage of default value configured in econda-monitor 
329     * @return PageView
330     */
331    public PageView addTarget(String target, String additionalInfo, double score, TargetRule rule) {
332        try {
333            JSONArray array = new JSONArray();
334            array.put(target);
335            array.put(additionalInfo);
336            array.put(score);
337            array.put(rule.transmitKey);
338            return addProperty("Target", array);
339        } catch (JSONException e) {
340            throw new RuntimeException(e);
341        }
342    }
343
344
345    /**
346     * Adds a orderProcess property.<br><br>
347     *
348     * @param processStep A Label to identify the step in the order-process by
349     *        convention this is a positive int followed by an underscore and
350     *        the label e.g. "1_Kundendaten", "2_Zahlungsoptionen", ..
351     * @return PageView
352     */
353    public PageView addOrderProcess(String processStep) {
354        return addProperty("orderProcess", processStep);
355    }
356
357
358    /**
359     * Adds a register property.<br>
360     *
361     * This method always scramble the userId with a MD5 Hash for you.
362     * This is perhaps necessary to fulfill the German "Datenschutzgesetz".
363     *
364     * @param userId UserID or email - must be a unique identifier
365     * @param resultCode 0 if success, positive int for error numbers
366     *
367     * @return PageView
368     */
369    public PageView addRegister(String userId, int resultCode) {
370        return addRegister(userId, resultCode, true);
371    }
372
373
374    /**
375     * Adds a register property.<br>
376     *
377     * Set Parameter scramble to true, if you want that this library is hashing the userId for you.
378     * This is perhaps necessary to fulfill the German "Datenschutzgesetz".
379     *
380     * @param userId UserID or email - must be a unique identifier
381     * @param resultCode 0 if success, positive int for error numbers
382     * @param scramble whether to hash userId or use given userId
383     *
384     * @return PageView
385     */
386    public PageView addRegister(String userId, int resultCode, boolean scramble) {
387        if (scramble){
388            userId = SimpleDigest.digest(userId);
389        }
390
391        JSONArray array = new JSONArray();
392        array.put(userId);
393        array.put(resultCode);
394        return addProperty("register", array);
395    }
396
397
398    /**
399     * Adds a search property
400     *
401     * @param queryString the search string
402     * @param numberOfHits the number of hits found
403     * @return PageView
404     */
405    public PageView addSearch(String queryString, int numberOfHits) {
406        JSONArray array = new JSONArray();
407        array.put(queryString);
408        array.put(numberOfHits);
409        return addProperty("search", array);
410    }
411
412    /**
413     * Adds a siteid property. Some shop systems can
414     * handle multiple sites. If we have to distinguish them, we set the
415     * siteid for the actual shop.
416     *
417     * @param siteId unique identifier for this site
418     * @return PageView
419     */
420    public PageView addSiteId(String siteId) {
421        return addProperty("siteid", siteId);
422    }
423
424    /**
425     * Adds a intern property.
426     *
427     * @param intern
428     * @return PageView
429     */
430    public PageView addIntern(String intern) {
431        return addProperty("intern", intern);
432    }
433
434    /**
435     * Adds a marker property.
436     *
437     * @param marker
438     * @return PageView
439     */
440    public PageView addMarker(String marker) {
441        return addProperty("marker", marker);
442    }
443
444    /**
445     * Adds a country property. Some shop systems can
446     * handle multiple sites. If we have to distinguish them, we set the
447     * countryId for the actual shop.
448     *
449     * @param countryId unique identifier for this country
450     * @return PageView
451     */
452    public PageView addCountryId(String countryId) {
453        return addProperty("countryid", countryId);
454    }
455
456    /**
457     * Adds a language property. Some shop systems can
458     * handle multiple languages. If we have to distinguish them, we set the
459     * language.
460     *
461     * @param langId unique identifier for this language
462     * @return PageView
463     */
464    public PageView addLangId(String langId) {
465        return addProperty("langid", langId);
466    }
467    
468    /**
469     * adds a addToBasket - Event to this page
470     *
471     * @param item the BasketItem this Event belongs to
472     * @return PageView
473     */
474    public PageView addProductAddToBasket(BasketItem item) {
475        return addProductCustomEvent(item, "c_add");
476    }
477
478    /**
479     * adds a removeFromBasket - Event to this page
480     *
481     * @param item the BasketItem this Event belongs to
482     * @return PageView
483     */
484    public PageView addProductRemoveFromBasket(BasketItem item) {
485        return addProductCustomEvent(item, "c_rmv");
486    }
487
488
489    /**
490     * Adds a ec_Event property.
491     * Stored as array of array. 
492     * Array contains the eventType = 'buy' and all BasketItem-Informations.
493     *
494     * @param item BasketItem
495     * @return PageView
496     */
497    public PageView addProductBuy(BasketItem item) {
498        return addProductCustomEvent(item, "buy");
499    }
500
501    /**
502     * Adds a ec_Event property.
503     * Stored as array of array. 
504     * Array contains the eventType = 'view' and all BasketItem-Informations.
505     *
506     * @param item BasketItem this detail View belongs to
507     * @return PageView
508     */
509    public PageView addProductView(BasketItem item) {
510        return addProductCustomEvent(item, "view");
511    }
512
513
514    /**
515     * Adds a ec_Event property with custom eventType.
516     * Stored as array of array. 
517     * Array contains the custom eventType and all BasketItem-Informations.
518     *
519     * @param item the item the EC-Event belongs to
520     * @param eventType type of the event like c_add, c_rmv, view, buy
521     * @return PageView
522     */
523    public PageView addProductCustomEvent(BasketItem item, String eventType) {
524        try {
525
526            JSONObject obj = new JSONObject();
527
528            obj.put("type", eventType);
529            item.appendBasketItemProperties(obj);
530
531            JSONArray outerArray = properties.optJSONArray("ec_Event");
532            if (outerArray == null) {
533                outerArray = new JSONArray();
534                properties.put("ec_Event", outerArray);
535            }
536
537            outerArray.put(obj);
538            return this;
539        } catch (JSONException e) {
540            throw new RuntimeException(e);
541        }
542
543    }
544
545    JSONObject getProperties() {
546        return properties;
547    }
548}